Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
authorDavid S. Miller <davem@davemloft.net>
Mon, 1 Jul 2013 00:35:13 +0000 (17:35 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 1 Jul 2013 00:35:13 +0000 (17:35 -0700)
Pablo Neira Ayuso says:

====================
The following batch contains Netfilter/IPVS updates for net-next,
they are:

* Enforce policy to several nfnetlink subsystem, from Daniel
  Borkmann.

* Use xt_socket to match the third packet (to perform simplistic
  socket-based stateful filtering), from Eric Dumazet.

* Avoid large timeout for picked up from the middle TCP flows,
  from Florian Westphal.

* Exclude IPVS from struct net if IPVS is disabled and removal
  of unnecessary included header file, from JunweiZhang.

* Release SCTP connection immediately under load, to mimic current
  TCP behaviour, from Julian Anastasov.

* Replace and enhance SCTP state machine, from Julian Anastasov.

* Add tweak to reduce sync traffic in the presence of persistence,
  also from Julian Anastasov.

* Add tweak for the IPVS SH scheduler not to reject connections
  directed to a server, choose a new one instead, from Alexander
  Frolkin.

* Add support for sloppy TCP and SCTP modes, that creates state
  information on any packet, not only initial handshake packets,
  from Alexander Frolkin.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
354 files changed:
Documentation/DocBook/80211.tmpl
Documentation/devicetree/bindings/net/arc_emac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
Documentation/networking/bonding.txt
Documentation/networking/netlink_mmap.txt
Documentation/sysctl/net.txt
arch/arm/boot/dts/imx28-evk.dts
arch/arm/mach-imx/devices-imx25.h
arch/arm/mach-imx/devices-imx35.h
arch/arm/mach-imx/devices/devices-common.h
arch/arm/mach-imx/devices/platform-flexcan.c
arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c
arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c
arch/arm/mach-imx/mach-mx25_3ds.c
arch/arm/mach-imx/mach-pcm043.c
arch/arm/mach-mxs/mach-mxs.c
drivers/bcma/Kconfig
drivers/bcma/core.c
drivers/bcma/driver_chipcommon_sflash.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btusb.c
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bonding.h
drivers/net/can/at91_can.c
drivers/net/can/flexcan.c
drivers/net/ethernet/8390/ne2k-pci.c
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/apple/bmac.c
drivers/net/ethernet/arc/Kconfig [new file with mode: 0644]
drivers/net/ethernet/arc/Makefile [new file with mode: 0644]
drivers/net/ethernet/arc/emac.h [new file with mode: 0644]
drivers/net/ethernet/arc/emac_main.c [new file with mode: 0644]
drivers/net/ethernet/arc/emac_mdio.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/korina.c
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c
drivers/net/ethernet/mellanox/mlx4/en_main.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c
drivers/net/ethernet/realtek/8139cp.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/efx.h
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/filter.c
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/nic.c
drivers/net/ethernet/sfc/nic.h
drivers/net/ethernet/sfc/ptp.c
drivers/net/ethernet/sfc/rx.c
drivers/net/ethernet/sun/sunbmac.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/davinci_mdio.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/via/via-velocity.c
drivers/net/macvlan.c
drivers/net/macvtap.c
drivers/net/nlmon.c [new file with mode: 0644]
drivers/net/usb/ax88179_178a.c
drivers/net/wireless/ath/Kconfig
drivers/net/wireless/ath/Makefile
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/Makefile [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/bmi.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/bmi.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/ce.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/ce.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/core.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/core.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/debug.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/debug.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/hif.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htc.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt_rx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/htt_tx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/hw.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/mac.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/mac.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/pci.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/pci.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/rx_desc.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/targaddrs.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/trace.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/trace.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/txrx.h [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/wmi.c [new file with mode: 0644]
drivers/net/wireless/ath/ath10k/wmi.h [new file with mode: 0644]
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath9k/ani.c
drivers/net/wireless/ath/ath9k/ani.h
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
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/main.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/carl9170.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wil6210/Kconfig
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/cw1200/main.c
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
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/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/tx.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/tx.c
drivers/net/wireless/mwifiex/11h.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/Makefile
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/orinoco/orinoco_pci.h
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtlwifi/rtl8192cu/rf.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/reg.h
drivers/net/wireless/ti/wlcore/Makefile
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/sysfs.c [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/sysfs.h [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/xen-netback/netback.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/mei_phy.c
drivers/nfc/microread/microread.c
drivers/nfc/nfcsim.c [new file with mode: 0644]
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544/pn544.c
drivers/s390/net/netiucv.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/scsi/libiscsi_tcp.c
drivers/ssb/driver_chipcommon_sflash.c
drivers/ssb/main.c
drivers/ssb/pcihost_wrapper.c
drivers/ssb/ssb_private.h
fs/select.c
include/linux/can/platform/flexcan.h [deleted file]
include/linux/ieee80211.h
include/linux/if_macvlan.h
include/linux/ktime.h
include/linux/netlink.h
include/linux/skbuff.h
include/linux/ssb/ssb_driver_mips.h
include/net/addrconf.h
include/net/cfg80211.h
include/net/if_inet6.h
include/net/ip_fib.h
include/net/ip_tunnels.h
include/net/ll_poll.h
include/net/mac80211.h
include/net/nfc/hci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/sctp/sctp.h
include/net/sctp/structs.h
include/net/tcp.h
include/uapi/asm-generic/poll.h
include/uapi/linux/if_arp.h
include/uapi/linux/nfc.h
include/uapi/linux/nl80211.h
include/uapi/linux/snmp.h
net/batman-adv/main.c
net/bridge/br_multicast.c
net/core/dev.c
net/core/skbuff.c
net/core/sock.c
net/core/sysctl_net_core.c
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c
net/ipv4/ip_tunnel.c
net/ipv4/route.c
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/mcast.c
net/ipv6/sit.c
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tx.c
net/mac80211/util.c
net/netlink/af_netlink.c
net/nfc/core.c
net/nfc/hci/core.c
net/nfc/llcp.h
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/llcp_sock.c
net/nfc/nci/Kconfig
net/nfc/nci/Makefile
net/nfc/nci/core.c
net/nfc/nci/data.c
net/nfc/nci/spi.c [new file with mode: 0644]
net/nfc/netlink.c
net/nfc/nfc.h
net/openvswitch/vport-gre.c
net/openvswitch/vport.c
net/sctp/associola.c
net/sctp/endpointola.c
net/sctp/proc.c
net/sctp/sm_make_chunk.c
net/sctp/socket.c
net/socket.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/vmci_transport.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/wext-sme.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_proc.c

index ebe8969..49267ea 100644 (file)
 !Finclude/net/cfg80211.h cfg80211_ibss_params
 !Finclude/net/cfg80211.h cfg80211_connect_params
 !Finclude/net/cfg80211.h cfg80211_pmksa
-!Finclude/net/cfg80211.h cfg80211_send_rx_auth
-!Finclude/net/cfg80211.h cfg80211_send_auth_timeout
-!Finclude/net/cfg80211.h cfg80211_send_rx_assoc
-!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
-!Finclude/net/cfg80211.h cfg80211_send_deauth
-!Finclude/net/cfg80211.h cfg80211_send_disassoc
+!Finclude/net/cfg80211.h cfg80211_rx_mlme_mgmt
+!Finclude/net/cfg80211.h cfg80211_auth_timeout
+!Finclude/net/cfg80211.h cfg80211_rx_assoc_resp
+!Finclude/net/cfg80211.h cfg80211_assoc_timeout
+!Finclude/net/cfg80211.h cfg80211_tx_mlme_mgmt
 !Finclude/net/cfg80211.h cfg80211_ibss_joined
 !Finclude/net/cfg80211.h cfg80211_connect_result
 !Finclude/net/cfg80211.h cfg80211_roamed
diff --git a/Documentation/devicetree/bindings/net/arc_emac.txt b/Documentation/devicetree/bindings/net/arc_emac.txt
new file mode 100644 (file)
index 0000000..bcbc3f0
--- /dev/null
@@ -0,0 +1,38 @@
+* Synopsys ARC EMAC 10/100 Ethernet driver (EMAC)
+
+Required properties:
+- compatible: Should be "snps,arc-emac"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the EMAC interrupts
+- clock-frequency: CPU frequency. It is needed to calculate and set polling
+period of EMAC.
+- max-speed: Maximum supported data-rate in Mbit/s. In some HW configurations
+bandwidth of external memory controller might be a limiting factor. That's why
+it's required to specify which data-rate is supported on current SoC or FPGA.
+For example if only 10 Mbit/s is supported (10BASE-T) set "10". If 100 Mbit/s is
+supported (100BASE-TX) set "100".
+- phy: PHY device attached to the EMAC via MDIO bus
+
+Child nodes of the driver are the individual PHY devices connected to the
+MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus.
+
+Optional properties:
+- mac-address: 6 bytes, mac address
+
+Examples:
+
+       ethernet@c0fc2000 {
+               compatible = "snps,arc-emac";
+               reg = <0xc0fc2000 0x3c>;
+               interrupts = <6>;
+               mac-address = [ 00 11 22 33 44 55 ];
+               clock-frequency = <80000000>;
+               max-speed = <100>;
+               phy = <&phy0>;
+
+               #address-cells = <1>;
+               #size-cells = <0>;
+               phy0: ethernet-phy@0 {
+                       reg = <1>;
+               };
+       };
index 8ff324e..56d6cc3 100644 (file)
@@ -16,6 +16,8 @@ Optional properties:
 
 - clock-frequency : The oscillator frequency driving the flexcan device
 
+- xceiver-supply: Regulator that powers the CAN transceiver
+
 Example:
 
        can@1c000 {
index e7454fc..87bbcfe 100644 (file)
@@ -303,6 +303,12 @@ arp_validate
        such a situation, validation of backup slaves must be
        disabled.
 
+       The validation of ARP requests on backup slaves is mainly
+       helping bonding to decide which slaves are more likely to
+       work in case of the active slave failure, it doesn't really
+       guarantee that the backup slave will work if it's selected
+       as the next active slave.
+
        This option is useful in network configurations in which
        multiple bonding hosts are concurrently issuing ARPs to one or
        more targets beyond a common switch.  Should the link between
@@ -315,6 +321,25 @@ arp_validate
 
        This option was added in bonding version 3.1.0.
 
+arp_all_targets
+
+       Specifies the quantity of arp_ip_targets that must be reachable
+       in order for the ARP monitor to consider a slave as being up.
+       This option affects only active-backup mode for slaves with
+       arp_validation enabled.
+
+       Possible values are:
+
+       any or 0
+
+               consider the slave up only when any of the arp_ip_targets
+               is reachable
+
+       all or 1
+
+               consider the slave up only when all of the arp_ip_targets
+               are reachable
+
 downdelay
 
        Specifies the time, in milliseconds, to wait before disabling
index e6088ba..5cc6005 100644 (file)
@@ -274,9 +274,9 @@ This example assumes some ring parameters of the ring setup are available.
                        /* Get next frame header */
                        hdr = rx_ring + frame_offset;
 
-                       if (hdr->nm_status == NL_MMAP_STATUS_VALID)
+                       if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
                                /* Regular memory mapped frame */
-                               nlh = (void *hdr) + NL_MMAP_HDRLEN;
+                               nlh = (void *)hdr + NL_MMAP_HDRLEN;
                                len = hdr->nm_len;
 
                                /* Release empty message immediately. May happen
index 5369879..e658bbf 100644 (file)
@@ -50,13 +50,27 @@ The maximum number of packets that kernel can handle on a NAPI interrupt,
 it's a Per-CPU variable.
 Default: 64
 
-low_latency_poll
+low_latency_read
 ----------------
-Low latency busy poll timeout. (needs CONFIG_NET_LL_RX_POLL)
+Low latency busy poll timeout for socket reads. (needs CONFIG_NET_LL_RX_POLL)
 Approximate time in us to spin waiting for packets on the device queue.
+This sets the default value of the SO_LL socket option.
+Can be set or overridden per socket by setting socket option SO_LL.
 Recommended value is 50. May increase power usage.
 Default: 0 (off)
 
+low_latency_poll
+----------------
+Low latency busy poll timeout for poll and select. (needs CONFIG_NET_LL_RX_POLL)
+Approximate time in us to spin waiting for packets on the device queue.
+Recommended value depends on the number of sockets you poll on.
+For several sockets 50, for several hundreds 100.
+For more than that you probably want to use epoll.
+Note that only sockets with SO_LL set will be busy polled, so you want to either
+selectively set SO_LL on those sockets or set sysctl.net.low_latency_read globally.
+May increase power usage.
+Default: 0 (off)
+
 rmem_default
 ------------
 
index 3637bf3..1f0d38d 100644 (file)
                        can0: can@80032000 {
                                pinctrl-names = "default";
                                pinctrl-0 = <&can0_pins_a>;
+                               xceiver-supply = <&reg_can_3v3>;
                                status = "okay";
                        };
 
                        can1: can@80034000 {
                                pinctrl-names = "default";
                                pinctrl-0 = <&can1_pins_a>;
+                               xceiver-supply = <&reg_can_3v3>;
                                status = "okay";
                        };
                };
                        gpio = <&gpio3 30 0>;
                        enable-active-high;
                };
+
+               reg_can_3v3: can-3v3 {
+                       compatible = "regulator-fixed";
+                       regulator-name = "can-3v3";
+                       regulator-min-microvolt = <3300000>;
+                       regulator-max-microvolt = <3300000>;
+                       gpio = <&gpio2 13 0>;
+                       enable-active-high;
+               };
+
        };
 
        sound {
index 0d2922b..769563f 100644 (file)
@@ -13,10 +13,10 @@ extern const struct imx_fec_data imx25_fec_data;
        imx_add_fec(&imx25_fec_data, pdata)
 
 extern const struct imx_flexcan_data imx25_flexcan_data[];
-#define imx25_add_flexcan(id, pdata)   \
-       imx_add_flexcan(&imx25_flexcan_data[id], pdata)
-#define imx25_add_flexcan0(pdata)      imx25_add_flexcan(0, pdata)
-#define imx25_add_flexcan1(pdata)      imx25_add_flexcan(1, pdata)
+#define imx25_add_flexcan(id)  \
+       imx_add_flexcan(&imx25_flexcan_data[id])
+#define imx25_add_flexcan0()           imx25_add_flexcan(0)
+#define imx25_add_flexcan1()           imx25_add_flexcan(1)
 
 extern const struct imx_fsl_usb2_udc_data imx25_fsl_usb2_udc_data;
 #define imx25_add_fsl_usb2_udc(pdata)  \
index e2675f1..780d824 100644 (file)
@@ -17,10 +17,10 @@ extern const struct imx_fsl_usb2_udc_data imx35_fsl_usb2_udc_data;
        imx_add_fsl_usb2_udc(&imx35_fsl_usb2_udc_data, pdata)
 
 extern const struct imx_flexcan_data imx35_flexcan_data[];
-#define imx35_add_flexcan(id, pdata)   \
-       imx_add_flexcan(&imx35_flexcan_data[id], pdata)
-#define imx35_add_flexcan0(pdata)      imx35_add_flexcan(0, pdata)
-#define imx35_add_flexcan1(pdata)      imx35_add_flexcan(1, pdata)
+#define imx35_add_flexcan(id)  \
+       imx_add_flexcan(&imx35_flexcan_data[id])
+#define imx35_add_flexcan0()           imx35_add_flexcan(0)
+#define imx35_add_flexcan1()           imx35_add_flexcan(1)
 
 extern const struct imx_imx2_wdt_data imx35_imx2_wdt_data;
 #define imx35_add_imx2_wdt()       \
index 453e20b..c13b76b 100644 (file)
@@ -50,7 +50,6 @@ struct platform_device *__init imx_add_fec(
                const struct imx_fec_data *data,
                const struct fec_platform_data *pdata);
 
-#include <linux/can/platform/flexcan.h>
 struct imx_flexcan_data {
        int id;
        resource_size_t iobase;
@@ -58,8 +57,7 @@ struct imx_flexcan_data {
        resource_size_t irq;
 };
 struct platform_device *__init imx_add_flexcan(
-               const struct imx_flexcan_data *data,
-               const struct flexcan_platform_data *pdata);
+               const struct imx_flexcan_data *data);
 
 #include <linux/fsl_devices.h>
 struct imx_fsl_usb2_udc_data {
index 1078bf0..55d61ea 100644 (file)
@@ -38,8 +38,7 @@ const struct imx_flexcan_data imx35_flexcan_data[] __initconst = {
 #endif /* ifdef CONFIG_SOC_IMX35 */
 
 struct platform_device *__init imx_add_flexcan(
-               const struct imx_flexcan_data *data,
-               const struct flexcan_platform_data *pdata)
+               const struct imx_flexcan_data *data)
 {
        struct resource res[] = {
                {
@@ -54,5 +53,5 @@ struct platform_device *__init imx_add_flexcan(
        };
 
        return imx_add_platform_device("flexcan", data->id,
-                       res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
+                       res, ARRAY_SIZE(res), NULL, 0);
 }
index e2b70f4..e77cc3a 100644 (file)
@@ -279,7 +279,7 @@ void __init eukrea_mbimxsd25_baseboard_init(void)
        imx25_add_imx_fb(&eukrea_mximxsd_fb_pdata);
        imx25_add_imx_ssi(0, &eukrea_mbimxsd_ssi_pdata);
 
-       imx25_add_flexcan1(NULL);
+       imx25_add_flexcan1();
        imx25_add_sdhci_esdhc_imx(0, &sd1_pdata);
 
        gpio_request(GPIO_LED1, "LED1");
index 5a2d5ef..14d6c82 100644 (file)
@@ -287,7 +287,7 @@ void __init eukrea_mbimxsd35_baseboard_init(void)
 
        imx35_add_imx_ssi(0, &eukrea_mbimxsd_ssi_pdata);
 
-       imx35_add_flexcan1(NULL);
+       imx35_add_flexcan1();
        imx35_add_sdhci_esdhc_imx(0, &sd1_pdata);
 
        gpio_request(GPIO_LED1, "LED1");
index 8bcda68..13490c2 100644 (file)
@@ -249,7 +249,7 @@ static void __init mx25pdk_init(void)
        imx25_add_imx_i2c0(&mx25_3ds_i2c0_data);
 
        gpio_request_one(MX25PDK_CAN_PWDN, GPIOF_OUT_INIT_LOW, "can-pwdn");
-       imx25_add_flexcan0(NULL);
+       imx25_add_flexcan0();
 }
 
 static void __init mx25pdk_timer_init(void)
index 8ed533f..b726cb1 100644 (file)
@@ -385,7 +385,7 @@ static void __init pcm043_init(void)
        if (!otg_mode_host)
                imx35_add_fsl_usb2_udc(&otg_device_pdata);
 
-       imx35_add_flexcan1(NULL);
+       imx35_add_flexcan1();
        imx35_add_sdhci_esdhc_imx(0, &sd1_pdata);
 }
 
index 5b62b64..97b8a44 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/clk/mxs.h>
 #include <linux/clkdev.h>
 #include <linux/clocksource.h>
-#include <linux/can/platform/flexcan.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
@@ -60,41 +59,6 @@ static inline void __mxs_togl(u32 mask, void __iomem *reg)
        __raw_writel(mask, reg + MXS_TOG_ADDR);
 }
 
-/*
- * MX28EVK_FLEXCAN_SWITCH is shared between both flexcan controllers
- */
-#define MX28EVK_FLEXCAN_SWITCH MXS_GPIO_NR(2, 13)
-
-static int flexcan0_en, flexcan1_en;
-
-static void mx28evk_flexcan_switch(void)
-{
-       if (flexcan0_en || flexcan1_en)
-               gpio_set_value(MX28EVK_FLEXCAN_SWITCH, 1);
-       else
-               gpio_set_value(MX28EVK_FLEXCAN_SWITCH, 0);
-}
-
-static void mx28evk_flexcan0_switch(int enable)
-{
-       flexcan0_en = enable;
-       mx28evk_flexcan_switch();
-}
-
-static void mx28evk_flexcan1_switch(int enable)
-{
-       flexcan1_en = enable;
-       mx28evk_flexcan_switch();
-}
-
-static struct flexcan_platform_data flexcan_pdata[2];
-
-static struct of_dev_auxdata mxs_auxdata_lookup[] __initdata = {
-       OF_DEV_AUXDATA("fsl,imx28-flexcan", 0x80032000, NULL, &flexcan_pdata[0]),
-       OF_DEV_AUXDATA("fsl,imx28-flexcan", 0x80034000, NULL, &flexcan_pdata[1]),
-       { /* sentinel */ }
-};
-
 #define OCOTP_WORD_OFFSET              0x20
 #define OCOTP_WORD_COUNT               0x20
 
@@ -254,15 +218,6 @@ static void __init imx28_evk_init(void)
        mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0);
 }
 
-static void __init imx28_evk_post_init(void)
-{
-       if (!gpio_request_one(MX28EVK_FLEXCAN_SWITCH, GPIOF_DIR_OUT,
-                             "flexcan-switch")) {
-               flexcan_pdata[0].transceiver_switch = mx28evk_flexcan0_switch;
-               flexcan_pdata[1].transceiver_switch = mx28evk_flexcan1_switch;
-       }
-}
-
 static int apx4devkit_phy_fixup(struct phy_device *phy)
 {
        phy->dev_flags |= MICREL_PHY_50MHZ_CLK;
@@ -374,13 +329,10 @@ static void __init mxs_machine_init(void)
                cfa10049_init();
 
        of_platform_populate(NULL, of_default_bus_match_table,
-                            mxs_auxdata_lookup, NULL);
+                            NULL, NULL);
 
        if (of_machine_is_compatible("karo,tx28"))
                tx28_post_init();
-
-       if (of_machine_is_compatible("fsl,imx28-evk"))
-               imx28_evk_post_init();
 }
 
 #define MX23_CLKCTRL_RESET_OFFSET      0x120
index 8b4221c..380a200 100644 (file)
@@ -26,6 +26,7 @@ config BCMA_HOST_PCI_POSSIBLE
 config BCMA_HOST_PCI
        bool "Support for BCMA on PCI-host bus"
        depends on BCMA_HOST_PCI_POSSIBLE
+       default y
 
 config BCMA_DRIVER_PCI_HOSTMODE
        bool "Driver for PCI core working in hostmode"
index 17b26ce..37a5ffe 100644 (file)
@@ -9,6 +9,25 @@
 #include <linux/export.h>
 #include <linux/bcma/bcma.h>
 
+static bool bcma_core_wait_value(struct bcma_device *core, u16 reg, u32 mask,
+                                u32 value, int timeout)
+{
+       unsigned long deadline = jiffies + timeout;
+       u32 val;
+
+       do {
+               val = bcma_aread32(core, reg);
+               if ((val & mask) == value)
+                       return true;
+               cpu_relax();
+               udelay(10);
+       } while (!time_after_eq(jiffies, deadline));
+
+       bcma_warn(core->bus, "Timeout waiting for register 0x%04X!\n", reg);
+
+       return false;
+}
+
 bool bcma_core_is_enabled(struct bcma_device *core)
 {
        if ((bcma_aread32(core, BCMA_IOCTL) & (BCMA_IOCTL_CLK | BCMA_IOCTL_FGC))
@@ -25,13 +44,15 @@ void bcma_core_disable(struct bcma_device *core, u32 flags)
        if (bcma_aread32(core, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
                return;
 
-       bcma_awrite32(core, BCMA_IOCTL, flags);
-       bcma_aread32(core, BCMA_IOCTL);
-       udelay(10);
+       bcma_core_wait_value(core, BCMA_RESET_ST, ~0, 0, 300);
 
        bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
        bcma_aread32(core, BCMA_RESET_CTL);
        udelay(1);
+
+       bcma_awrite32(core, BCMA_IOCTL, flags);
+       bcma_aread32(core, BCMA_IOCTL);
+       udelay(10);
 }
 EXPORT_SYMBOL_GPL(bcma_core_disable);
 
@@ -43,6 +64,7 @@ int bcma_core_enable(struct bcma_device *core, u32 flags)
        bcma_aread32(core, BCMA_IOCTL);
 
        bcma_awrite32(core, BCMA_RESET_CTL, 0);
+       bcma_aread32(core, BCMA_RESET_CTL);
        udelay(1);
 
        bcma_awrite32(core, BCMA_IOCTL, (BCMA_IOCTL_CLK | flags));
index e6ed4fe..4d07cce 100644 (file)
@@ -30,7 +30,7 @@ struct bcma_sflash_tbl_e {
        u16 numblocks;
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { "M25P20", 0x11, 0x10000, 4, },
        { "M25P40", 0x12, 0x10000, 8, },
 
@@ -41,7 +41,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { "SST25WF512", 1, 0x1000, 16, },
        { "SST25VF512", 0x48, 0x1000, 16, },
        { "SST25WF010", 2, 0x1000, 32, },
@@ -59,7 +59,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { 0 },
 };
 
-static struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
+static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
        { "AT45DB011", 0xc, 256, 512, },
        { "AT45DB021", 0x14, 256, 1024, },
        { "AT45DB041", 0x1c, 256, 2048, },
@@ -89,7 +89,7 @@ int bcma_sflash_init(struct bcma_drv_cc *cc)
 {
        struct bcma_bus *bus = cc->core->bus;
        struct bcma_sflash *sflash = &cc->sflash;
-       struct bcma_sflash_tbl_e *e;
+       const struct bcma_sflash_tbl_e *e;
        u32 id, id2;
 
        switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
index 13693b7..75c2626 100644 (file)
@@ -554,6 +554,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
        skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
        if (skb == NULL) {
                BT_ERR("No free skb");
+               ret = -ENOMEM;
                goto exit;
        }
 
index 7a7e5f8..81f1275 100644 (file)
@@ -57,6 +57,9 @@ static struct usb_device_id btusb_table[] = {
        /* Apple-specific (Broadcom) devices */
        { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
 
+       /* MediaTek MT76x0E */
+       { USB_DEVICE(0x0e8d, 0x763f) },
+
        /* Broadcom SoftSailing reporting vendor specific */
        { USB_DEVICE(0x0a5c, 0x21e1) },
 
index 00aba08..b45b240 100644 (file)
@@ -240,6 +240,16 @@ config VIRTIO_NET
          This is the virtual network driver for virtio.  It can be used with
          lguest or QEMU based VMMs (like KVM or Xen).  Say Y or M.
 
+config NLMON
+       tristate "Virtual netlink monitoring device"
+       ---help---
+         This option enables a monitoring net device for netlink skbs. The
+         purpose of this is to analyze netlink messages with packet sockets.
+         Thus applications like tcpdump will be able to see local netlink
+         messages if they tap into the netlink device, record pcaps for further
+         diagnostics, etc. This is mostly intended for developers or support
+         to debug netlink issues. If unsure, say N.
+
 endif # NET_CORE
 
 config SUNGEM_PHY
index ef3d090..3fef8a8 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_TUN) += tun.o
 obj-$(CONFIG_VETH) += veth.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
 obj-$(CONFIG_VXLAN) += vxlan.o
+obj-$(CONFIG_NLMON) += nlmon.o
 
 #
 # Networking Drivers
index 3b31c19..742c193 100644 (file)
@@ -104,6 +104,7 @@ static char *xmit_hash_policy;
 static int arp_interval = BOND_LINK_ARP_INTERV;
 static char *arp_ip_target[BOND_MAX_ARP_TARGETS];
 static char *arp_validate;
+static char *arp_all_targets;
 static char *fail_over_mac;
 static int all_slaves_active = 0;
 static struct bond_params bonding_defaults;
@@ -166,6 +167,8 @@ module_param(arp_validate, charp, 0);
 MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes; "
                               "0 for none (default), 1 for active, "
                               "2 for backup, 3 for all");
+module_param(arp_all_targets, charp, 0);
+MODULE_PARM_DESC(arp_all_targets, "fail on any/all arp targets timeout; 0 for any (default), 1 for all");
 module_param(fail_over_mac, charp, 0);
 MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to "
                                "the same MAC; 0 for none (default), "
@@ -216,6 +219,12 @@ const struct bond_parm_tbl xmit_hashtype_tbl[] = {
 {      NULL,                   -1},
 };
 
+const struct bond_parm_tbl arp_all_targets_tbl[] = {
+{      "any",                  BOND_ARP_TARGETS_ANY},
+{      "all",                  BOND_ARP_TARGETS_ALL},
+{      NULL,                   -1},
+};
+
 const struct bond_parm_tbl arp_validate_tbl[] = {
 {      "none",                 BOND_ARP_VALIDATE_NONE},
 {      "active",               BOND_ARP_VALIDATE_ACTIVE},
@@ -824,6 +833,23 @@ static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
        }
 }
 
+/**
+ * bond_set_dev_addr - clone slave's address to bond
+ * @bond_dev: bond net device
+ * @slave_dev: slave net device
+ *
+ * Should be called with RTNL held.
+ */
+static void bond_set_dev_addr(struct net_device *bond_dev,
+                             struct net_device *slave_dev)
+{
+       pr_debug("bond_dev=%p slave_dev=%p slave_dev->addr_len=%d\n",
+                bond_dev, slave_dev, slave_dev->addr_len);
+       memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
+       bond_dev->addr_assign_type = NET_ADDR_STOLEN;
+       call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
+}
+
 /*
  * bond_do_fail_over_mac
  *
@@ -846,11 +872,9 @@ static void bond_do_fail_over_mac(struct bonding *bond,
        switch (bond->params.fail_over_mac) {
        case BOND_FOM_ACTIVE:
                if (new_active) {
-                       memcpy(bond->dev->dev_addr,  new_active->dev->dev_addr,
-                              new_active->dev->addr_len);
                        write_unlock_bh(&bond->curr_slave_lock);
                        read_unlock(&bond->lock);
-                       call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
+                       bond_set_dev_addr(bond->dev, new_active->dev);
                        read_lock(&bond->lock);
                        write_lock_bh(&bond->curr_slave_lock);
                }
@@ -1281,17 +1305,6 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
 
 /*---------------------------------- IOCTL ----------------------------------*/
 
-static void bond_set_dev_addr(struct net_device *bond_dev,
-                             struct net_device *slave_dev)
-{
-       pr_debug("bond_dev=%p\n", bond_dev);
-       pr_debug("slave_dev=%p\n", slave_dev);
-       pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
-       memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
-       bond_dev->addr_assign_type = NET_ADDR_SET;
-       call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev);
-}
-
 static netdev_features_t bond_fix_features(struct net_device *dev,
        netdev_features_t features)
 {
@@ -1373,8 +1386,6 @@ done:
 static void bond_setup_by_slave(struct net_device *bond_dev,
                                struct net_device *slave_dev)
 {
-       struct bonding *bond = netdev_priv(bond_dev);
-
        bond_dev->header_ops        = slave_dev->header_ops;
 
        bond_dev->type              = slave_dev->type;
@@ -1383,7 +1394,6 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
 
        memcpy(bond_dev->broadcast, slave_dev->broadcast,
                slave_dev->addr_len);
-       bond->setup_by_slave = 1;
 }
 
 /* On bonding slaves other than the currently active slave, suppress
@@ -1483,7 +1493,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        struct slave *new_slave = NULL;
        struct sockaddr addr;
        int link_reporting;
-       int res = 0;
+       int res = 0, i;
 
        if (!bond->params.use_carrier &&
            slave_dev->ethtool_ops->get_link == NULL &&
@@ -1590,7 +1600,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 
        /* If this is the first slave, then we need to set the master's hardware
         * address to be the same as the slave's. */
-       if (bond->slave_cnt == 0 && bond->dev_addr_from_first)
+       if (!bond->slave_cnt && bond->dev->addr_assign_type == NET_ADDR_RANDOM)
                bond_set_dev_addr(bond->dev, slave_dev);
 
        new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
@@ -1712,6 +1722,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 
        new_slave->last_arp_rx = jiffies -
                (msecs_to_jiffies(bond->params.arp_interval) + 1);
+       for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
+               new_slave->target_last_arp_rx[i] = new_slave->last_arp_rx;
 
        if (bond->params.miimon && !bond->params.use_carrier) {
                link_reporting = bond_check_dev_link(bond, slave_dev, 1);
@@ -2033,7 +2045,6 @@ static int __bond_release_one(struct net_device *bond_dev,
        if (bond->slave_cnt == 0) {
                bond_set_carrier(bond);
                eth_hw_addr_random(bond_dev);
-               bond->dev_addr_from_first = true;
 
                if (bond_vlan_used(bond)) {
                        pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n",
@@ -2611,18 +2622,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 sip, __be32 tip)
 {
        int i;
-       __be32 *targets = bond->params.arp_targets;
 
-       for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
-               pr_debug("bva: sip %pI4 tip %pI4 t[%d] %pI4 bhti(tip) %d\n",
-                        &sip, &tip, i, &targets[i],
-                        bond_has_this_ip(bond, tip));
-               if (sip == targets[i]) {
-                       if (bond_has_this_ip(bond, tip))
-                               slave->last_arp_rx = jiffies;
-                       return;
-               }
+       if (!sip || !bond_has_this_ip(bond, tip)) {
+               pr_debug("bva: sip %pI4 tip %pI4 not found\n", &sip, &tip);
+               return;
        }
+
+       i = bond_get_targets_ip(bond->params.arp_targets, sip);
+       if (i == -1) {
+               pr_debug("bva: sip %pI4 not found in targets\n", &sip);
+               return;
+       }
+       slave->last_arp_rx = jiffies;
+       slave->target_last_arp_rx[i] = jiffies;
 }
 
 static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -2637,6 +2649,10 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
                return RX_HANDLER_ANOTHER;
 
        read_lock(&bond->lock);
+
+       if (!slave_do_arp_validate(bond, slave))
+               goto out_unlock;
+
        alen = arp_hdr_len(bond->dev);
 
        pr_debug("bond_arp_rcv: bond %s skb->dev %s\n",
@@ -2676,10 +2692,17 @@ static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
         * configuration, the ARP probe will (hopefully) travel from
         * the active, through one switch, the router, then the other
         * switch before reaching the backup.
+        *
+        * We 'trust' the arp requests if there is an active slave and
+        * it received valid arp reply(s) after it became active. This
+        * is done to avoid endless looping when we can't reach the
+        * arp_ip_target and fool ourselves with our own arp requests.
         */
        if (bond_is_active_slave(slave))
                bond_validate_arp(bond, slave, sip, tip);
-       else
+       else if (bond->curr_active_slave &&
+                time_after(slave_last_rx(bond, bond->curr_active_slave),
+                           bond->curr_active_slave->jiffies))
                bond_validate_arp(bond, slave, tip, sip);
 
 out_unlock:
@@ -3164,7 +3187,7 @@ static int bond_slave_netdev_event(unsigned long event,
 
        switch (event) {
        case NETDEV_UNREGISTER:
-               if (bond->setup_by_slave)
+               if (bond_dev->type != ARPHRD_ETHER)
                        bond_release_and_destroy(bond_dev, slave_dev);
                else
                        bond_release(bond_dev, slave_dev);
@@ -4401,6 +4424,7 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
 static int bond_check_params(struct bond_params *params)
 {
        int arp_validate_value, fail_over_mac_value, primary_reselect_value, i;
+       int arp_all_targets_value;
 
        /*
         * Convert string parameters.
@@ -4591,7 +4615,11 @@ static int bond_check_params(struct bond_params *params)
                                   arp_ip_target[i]);
                        arp_interval = 0;
                } else {
-                       arp_target[arp_ip_count++] = ip;
+                       if (bond_get_targets_ip(arp_target, ip) == -1)
+                               arp_target[arp_ip_count++] = ip;
+                       else
+                               pr_warning("Warning: duplicate address %pI4 in arp_ip_target, skipping\n",
+                                          &ip);
                }
        }
 
@@ -4622,6 +4650,18 @@ static int bond_check_params(struct bond_params *params)
        } else
                arp_validate_value = 0;
 
+       arp_all_targets_value = 0;
+       if (arp_all_targets) {
+               arp_all_targets_value = bond_parse_parm(arp_all_targets,
+                                                       arp_all_targets_tbl);
+
+               if (arp_all_targets_value == -1) {
+                       pr_err("Error: invalid arp_all_targets_value \"%s\"\n",
+                              arp_all_targets);
+                       arp_all_targets_value = 0;
+               }
+       }
+
        if (miimon) {
                pr_info("MII link monitoring set to %d ms\n", miimon);
        } else if (arp_interval) {
@@ -4686,6 +4726,7 @@ static int bond_check_params(struct bond_params *params)
        params->num_peer_notif = num_peer_notif;
        params->arp_interval = arp_interval;
        params->arp_validate = arp_validate_value;
+       params->arp_all_targets = arp_all_targets_value;
        params->updelay = updelay;
        params->downdelay = downdelay;
        params->use_carrier = use_carrier;
@@ -4762,10 +4803,8 @@ static int bond_init(struct net_device *bond_dev)
 
        /* Ensure valid dev_addr */
        if (is_zero_ether_addr(bond_dev->dev_addr) &&
-           bond_dev->addr_assign_type == NET_ADDR_PERM) {
+           bond_dev->addr_assign_type == NET_ADDR_PERM)
                eth_hw_addr_random(bond_dev);
-               bond->dev_addr_from_first = true;
-       }
 
        return 0;
 }
@@ -4839,7 +4878,7 @@ static int __net_init bond_net_init(struct net *net)
 
        bond_create_proc_dir(bn);
        bond_create_sysfs(bn);
-       
+
        return 0;
 }
 
index f8bee4c..dc36a3d 100644 (file)
@@ -443,6 +443,44 @@ static ssize_t bonding_store_arp_validate(struct device *d,
 
 static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
                   bonding_store_arp_validate);
+/*
+ * Show and set arp_all_targets.
+ */
+static ssize_t bonding_show_arp_all_targets(struct device *d,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct bonding *bond = to_bond(d);
+       int value = bond->params.arp_all_targets;
+
+       return sprintf(buf, "%s %d\n", arp_all_targets_tbl[value].modename,
+                      value);
+}
+
+static ssize_t bonding_store_arp_all_targets(struct device *d,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t count)
+{
+       struct bonding *bond = to_bond(d);
+       int new_value;
+
+       new_value = bond_parse_parm(buf, arp_all_targets_tbl);
+       if (new_value < 0) {
+               pr_err("%s: Ignoring invalid arp_all_targets value %s\n",
+                      bond->dev->name, buf);
+               return -EINVAL;
+       }
+       pr_info("%s: setting arp_all_targets to %s (%d).\n",
+               bond->dev->name, arp_all_targets_tbl[new_value].modename,
+               new_value);
+
+       bond->params.arp_all_targets = new_value;
+
+       return count;
+}
+
+static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
+                  bonding_show_arp_all_targets, bonding_store_arp_all_targets);
 
 /*
  * Show and store fail_over_mac.  User only allowed to change the
@@ -590,10 +628,11 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                                         struct device_attribute *attr,
                                         const char *buf, size_t count)
 {
-       __be32 newtarget;
-       int i = 0, done = 0, ret = count;
        struct bonding *bond = to_bond(d);
-       __be32 *targets;
+       struct slave *slave;
+       __be32 newtarget, *targets;
+       unsigned long *targets_rx;
+       int ind, i, j, ret = -EINVAL;
 
        targets = bond->params.arp_targets;
        newtarget = in_aton(buf + 1);
@@ -602,57 +641,63 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
                        pr_err("%s: invalid ARP target %pI4 specified for addition\n",
                               bond->dev->name, &newtarget);
-                       ret = -EINVAL;
                        goto out;
                }
-               /* look for an empty slot to put the target in, and check for dupes */
-               for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) {
-                       if (targets[i] == newtarget) { /* duplicate */
-                               pr_err("%s: ARP target %pI4 is already present\n",
-                                      bond->dev->name, &newtarget);
-                               ret = -EINVAL;
-                               goto out;
-                       }
-                       if (targets[i] == 0) {
-                               pr_info("%s: adding ARP target %pI4.\n",
-                                       bond->dev->name, &newtarget);
-                               done = 1;
-                               targets[i] = newtarget;
-                       }
+
+               if (bond_get_targets_ip(targets, newtarget) != -1) { /* dup */
+                       pr_err("%s: ARP target %pI4 is already present\n",
+                              bond->dev->name, &newtarget);
+                       goto out;
                }
-               if (!done) {
+
+               ind = bond_get_targets_ip(targets, 0); /* first free slot */
+               if (ind == -1) {
                        pr_err("%s: ARP target table is full!\n",
                               bond->dev->name);
-                       ret = -EINVAL;
                        goto out;
                }
 
+               pr_info("%s: adding ARP target %pI4.\n", bond->dev->name,
+                        &newtarget);
+               /* not to race with bond_arp_rcv */
+               write_lock_bh(&bond->lock);
+               bond_for_each_slave(bond, slave, i)
+                       slave->target_last_arp_rx[ind] = jiffies;
+               targets[ind] = newtarget;
+               write_unlock_bh(&bond->lock);
        } else if (buf[0] == '-')       {
                if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
                        pr_err("%s: invalid ARP target %pI4 specified for removal\n",
                               bond->dev->name, &newtarget);
-                       ret = -EINVAL;
                        goto out;
                }
 
-               for (i = 0; (i < BOND_MAX_ARP_TARGETS) && !done; i++) {
-                       if (targets[i] == newtarget) {
-                               int j;
-                               pr_info("%s: removing ARP target %pI4.\n",
-                                       bond->dev->name, &newtarget);
-                               for (j = i; (j < (BOND_MAX_ARP_TARGETS-1)) && targets[j+1]; j++)
-                                       targets[j] = targets[j+1];
-
-                               targets[j] = 0;
-                               done = 1;
-                       }
-               }
-               if (!done) {
-                       pr_info("%s: unable to remove nonexistent ARP target %pI4.\n",
+               ind = bond_get_targets_ip(targets, newtarget);
+               if (ind == -1) {
+                       pr_err("%s: unable to remove nonexistent ARP target %pI4.\n",
                                bond->dev->name, &newtarget);
-                       ret = -EINVAL;
                        goto out;
                }
+
+               if (ind == 0 && !targets[1] && bond->params.arp_interval)
+                       pr_warn("%s: removing last arp target with arp_interval on\n",
+                               bond->dev->name);
+
+               pr_info("%s: removing ARP target %pI4.\n", bond->dev->name,
+                       &newtarget);
+
+               write_lock_bh(&bond->lock);
+               bond_for_each_slave(bond, slave, i) {
+                       targets_rx = slave->target_last_arp_rx;
+                       j = ind;
+                       for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++)
+                               targets_rx[j] = targets_rx[j+1];
+                       targets_rx[j] = 0;
+               }
+               for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
+                       targets[i] = targets[i+1];
+               targets[i] = 0;
+               write_unlock_bh(&bond->lock);
        } else {
                pr_err("no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n",
                       bond->dev->name);
@@ -660,6 +705,7 @@ static ssize_t bonding_store_arp_targets(struct device *d,
                goto out;
        }
 
+       ret = count;
 out:
        return ret;
 }
@@ -1635,6 +1681,7 @@ static struct attribute *per_bond_attrs[] = {
        &dev_attr_mode.attr,
        &dev_attr_fail_over_mac.attr,
        &dev_attr_arp_validate.attr,
+       &dev_attr_arp_all_targets.attr,
        &dev_attr_arp_interval.attr,
        &dev_attr_arp_ip_target.attr,
        &dev_attr_downdelay.attr,
index c990b42..42d1c65 100644 (file)
@@ -144,6 +144,7 @@ struct bond_params {
        u8 num_peer_notif;
        int arp_interval;
        int arp_validate;
+       int arp_all_targets;
        int use_carrier;
        int fail_over_mac;
        int updelay;
@@ -179,6 +180,7 @@ struct slave {
        int    delay;
        unsigned long jiffies;
        unsigned long last_arp_rx;
+       unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS];
        s8     link;    /* one of BOND_LINK_XXXX */
        s8     new_link;
        u8     backup:1,   /* indicates backup slave. Value corresponds with
@@ -224,7 +226,6 @@ struct bonding {
        rwlock_t lock;
        rwlock_t curr_slave_lock;
        u8       send_peer_notif;
-       s8       setup_by_slave;
        u8       igmp_retrans;
 #ifdef CONFIG_PROC_FS
        struct   proc_dir_entry *proc_entry;
@@ -247,7 +248,6 @@ struct bonding {
        /* debugging support via debugfs */
        struct   dentry *debug_dir;
 #endif /* CONFIG_DEBUG_FS */
-       bool    dev_addr_from_first;
 };
 
 static inline bool bond_vlan_used(struct bonding *bond)
@@ -322,6 +322,9 @@ static inline bool bond_is_active_slave(struct slave *slave)
 #define BOND_FOM_ACTIVE                        1
 #define BOND_FOM_FOLLOW                        2
 
+#define BOND_ARP_TARGETS_ANY           0
+#define BOND_ARP_TARGETS_ALL           1
+
 #define BOND_ARP_VALIDATE_NONE         0
 #define BOND_ARP_VALIDATE_ACTIVE       (1 << BOND_STATE_ACTIVE)
 #define BOND_ARP_VALIDATE_BACKUP       (1 << BOND_STATE_BACKUP)
@@ -334,11 +337,31 @@ static inline int slave_do_arp_validate(struct bonding *bond,
        return bond->params.arp_validate & (1 << bond_slave_state(slave));
 }
 
+/* Get the oldest arp which we've received on this slave for bond's
+ * arp_targets.
+ */
+static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
+                                                      struct slave *slave)
+{
+       int i = 1;
+       unsigned long ret = slave->target_last_arp_rx[0];
+
+       for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
+               if (time_before(slave->target_last_arp_rx[i], ret))
+                       ret = slave->target_last_arp_rx[i];
+
+       return ret;
+}
+
 static inline unsigned long slave_last_rx(struct bonding *bond,
                                        struct slave *slave)
 {
-       if (slave_do_arp_validate(bond, slave))
-               return slave->last_arp_rx;
+       if (slave_do_arp_validate(bond, slave)) {
+               if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
+                       return slave_oldest_target_arp_rx(bond, slave);
+               else
+                       return slave->last_arp_rx;
+       }
 
        return slave->dev->last_rx;
 }
@@ -464,12 +487,29 @@ static inline struct slave *bond_slave_has_mac(struct bonding *bond,
        return NULL;
 }
 
+/* Check if the ip is present in arp ip list, or first free slot if ip == 0
+ * Returns -1 if not found, index if found
+ */
+static inline int bond_get_targets_ip(__be32 *targets, __be32 ip)
+{
+       int i;
+
+       for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
+               if (targets[i] == ip)
+                       return i;
+               else if (targets[i] == 0)
+                       break;
+
+       return -1;
+}
+
 /* exported from bond_main.c */
 extern int bond_net_id;
 extern const struct bond_parm_tbl bond_lacp_tbl[];
 extern const struct bond_parm_tbl bond_mode_tbl[];
 extern const struct bond_parm_tbl xmit_hashtype_tbl[];
 extern const struct bond_parm_tbl arp_validate_tbl[];
+extern const struct bond_parm_tbl arp_all_targets_tbl[];
 extern const struct bond_parm_tbl fail_over_mac_tbl[];
 extern const struct bond_parm_tbl pri_reselect_tbl[];
 extern struct bond_parm_tbl ad_select_tbl[];
index ce8421a..dbbe97a 100644 (file)
@@ -1264,8 +1264,6 @@ static const struct of_device_id at91_can_dt_ids[] = {
        }
 };
 MODULE_DEVICE_TABLE(of, at91_can_dt_ids);
-#else
-#define at91_can_dt_ids NULL
 #endif
 
 static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev)
@@ -1424,7 +1422,7 @@ static struct platform_driver at91_can_driver = {
        .driver = {
                .name = KBUILD_MODNAME,
                .owner = THIS_MODULE,
-               .of_match_table = at91_can_dt_ids,
+               .of_match_table = of_match_ptr(at91_can_dt_ids),
        },
        .id_table = at91_can_id_table,
 };
index f873b9f..7b0be09 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/can/dev.h>
 #include <linux/can/error.h>
 #include <linux/can/led.h>
-#include <linux/can/platform/flexcan.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/if_arp.h>
@@ -37,6 +36,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 
 #define DRV_NAME                       "flexcan"
 
@@ -211,6 +211,7 @@ struct flexcan_priv {
        struct clk *clk_per;
        struct flexcan_platform_data *pdata;
        const struct flexcan_devtype_data *devtype_data;
+       struct regulator *reg_xceiver;
 };
 
 static struct flexcan_devtype_data fsl_p1010_devtype_data = {
@@ -258,15 +259,6 @@ static inline void flexcan_write(u32 val, void __iomem *addr)
 }
 #endif
 
-/*
- * Swtich transceiver on or off
- */
-static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on)
-{
-       if (priv->pdata && priv->pdata->transceiver_switch)
-               priv->pdata->transceiver_switch(on);
-}
-
 static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
                                              u32 reg_esr)
 {
@@ -799,7 +791,11 @@ static int flexcan_chip_start(struct net_device *dev)
        if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
                flexcan_write(0x0, &regs->rxfgmask);
 
-       flexcan_transceiver_switch(priv, 1);
+       if (priv->reg_xceiver)  {
+               err = regulator_enable(priv->reg_xceiver);
+               if (err)
+                       goto out;
+       }
 
        /* synchronize with the can bus */
        reg_mcr = flexcan_read(&regs->mcr);
@@ -842,7 +838,8 @@ static void flexcan_chip_stop(struct net_device *dev)
        reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT;
        flexcan_write(reg, &regs->mcr);
 
-       flexcan_transceiver_switch(priv, 0);
+       if (priv->reg_xceiver)
+               regulator_disable(priv->reg_xceiver);
        priv->can.state = CAN_STATE_STOPPED;
 
        return;
@@ -1084,6 +1081,10 @@ static int flexcan_probe(struct platform_device *pdev)
        priv->pdata = pdev->dev.platform_data;
        priv->devtype_data = devtype_data;
 
+       priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
+       if (IS_ERR(priv->reg_xceiver))
+               priv->reg_xceiver = NULL;
+
        netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
 
        dev_set_drvdata(&pdev->dev, dev);
index 587a885..9220108 100644 (file)
@@ -676,7 +676,7 @@ static int ne2k_pci_resume (struct pci_dev *pdev)
        struct net_device *dev = pci_get_drvdata (pdev);
        int rc;
 
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        rc = pci_enable_device(pdev);
index a989669..2037080 100644 (file)
@@ -24,6 +24,7 @@ source "drivers/net/ethernet/allwinner/Kconfig"
 source "drivers/net/ethernet/alteon/Kconfig"
 source "drivers/net/ethernet/amd/Kconfig"
 source "drivers/net/ethernet/apple/Kconfig"
+source "drivers/net/ethernet/arc/Kconfig"
 source "drivers/net/ethernet/atheros/Kconfig"
 source "drivers/net/ethernet/cadence/Kconfig"
 source "drivers/net/ethernet/adi/Kconfig"
index 009da27..390bd0b 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
 obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
 obj-$(CONFIG_NET_VENDOR_AMD) += amd/
 obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
+obj-$(CONFIG_NET_VENDOR_ARC) += arc/
 obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
 obj-$(CONFIG_NET_CADENCE) += cadence/
 obj-$(CONFIG_NET_BFIN) += adi/
index 714dcfe..a597b76 100644 (file)
@@ -1016,7 +1016,6 @@ static void bmac_set_multicast(struct net_device *dev)
 static void bmac_set_multicast(struct net_device *dev)
 {
        struct netdev_hw_addr *ha;
-       int i;
        unsigned short rx_cfg;
        u32 crc;
 
diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
new file mode 100644 (file)
index 0000000..514c57f
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# ARC EMAC network device configuration
+#
+
+config NET_VENDOR_ARC
+       bool "ARC devices"
+       default y
+       ---help---
+         If you have a network (Ethernet) card belonging to this class, say Y
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about ARC cards. If you say Y, you will be asked for
+         your specific card in the following questions.
+
+if NET_VENDOR_ARC
+
+config ARC_EMAC
+       tristate "ARC EMAC support"
+       select MII
+       select PHYLIB
+       depends on OF_IRQ
+       depends on OF_NET
+       ---help---
+         On some legacy ARC (Synopsys) FPGA boards such as ARCAngel4/ML50x
+         non-standard on-chip ethernet device ARC EMAC 10/100 is used.
+         Say Y here if you have such a board.  If unsure, say N.
+
+endif # NET_VENDOR_ARC
diff --git a/drivers/net/ethernet/arc/Makefile b/drivers/net/ethernet/arc/Makefile
new file mode 100644 (file)
index 0000000..00c8657
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the ARC network device drivers.
+#
+
+arc_emac-objs := emac_main.o emac_mdio.o
+obj-$(CONFIG_ARC_EMAC) += arc_emac.o
diff --git a/drivers/net/ethernet/arc/emac.h b/drivers/net/ethernet/arc/emac.h
new file mode 100644 (file)
index 0000000..dc08678
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com)
+ *
+ * Registers and bits definitions of ARC EMAC
+ */
+
+#ifndef ARC_EMAC_H
+#define ARC_EMAC_H
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+
+/* STATUS and ENABLE Register bit masks */
+#define TXINT_MASK     (1<<0)  /* Transmit interrupt */
+#define RXINT_MASK     (1<<1)  /* Receive interrupt */
+#define ERR_MASK       (1<<2)  /* Error interrupt */
+#define TXCH_MASK      (1<<3)  /* Transmit chaining error interrupt */
+#define MSER_MASK      (1<<4)  /* Missed packet counter error */
+#define RXCR_MASK      (1<<8)  /* RXCRCERR counter rolled over  */
+#define RXFR_MASK      (1<<9)  /* RXFRAMEERR counter rolled over */
+#define RXFL_MASK      (1<<10) /* RXOFLOWERR counter rolled over */
+#define MDIO_MASK      (1<<12) /* MDIO complete interrupt */
+#define TXPL_MASK      (1<<31) /* Force polling of BD by EMAC */
+
+/* CONTROL Register bit masks */
+#define EN_MASK                (1<<0)  /* VMAC enable */
+#define TXRN_MASK      (1<<3)  /* TX enable */
+#define RXRN_MASK      (1<<4)  /* RX enable */
+#define DSBC_MASK      (1<<8)  /* Disable receive broadcast */
+#define ENFL_MASK      (1<<10) /* Enable Full-duplex */
+#define PROM_MASK      (1<<11) /* Promiscuous mode */
+
+/* Buffer descriptor INFO bit masks */
+#define OWN_MASK       (1<<31) /* 0-CPU owns buffer, 1-EMAC owns buffer */
+#define FIRST_MASK     (1<<16) /* First buffer in chain */
+#define LAST_MASK      (1<<17) /* Last buffer in chain */
+#define LEN_MASK       0x000007FF      /* last 11 bits */
+#define CRLS           (1<<21)
+#define DEFR           (1<<22)
+#define DROP           (1<<23)
+#define RTRY           (1<<24)
+#define LTCL           (1<<28)
+#define UFLO           (1<<29)
+
+#define FOR_EMAC       OWN_MASK
+#define FOR_CPU                0
+
+/* ARC EMAC register set combines entries for MAC and MDIO */
+enum {
+       R_ID = 0,
+       R_STATUS,
+       R_ENABLE,
+       R_CTRL,
+       R_POLLRATE,
+       R_RXERR,
+       R_MISS,
+       R_TX_RING,
+       R_RX_RING,
+       R_ADDRL,
+       R_ADDRH,
+       R_LAFL,
+       R_LAFH,
+       R_MDIO,
+};
+
+#define TX_TIMEOUT             (400*HZ/1000)   /* Transmission timeout */
+
+#define ARC_EMAC_NAPI_WEIGHT   40              /* Workload for NAPI */
+
+#define EMAC_BUFFER_SIZE       1536            /* EMAC buffer size */
+
+/**
+ * struct arc_emac_bd - EMAC buffer descriptor (BD).
+ *
+ * @info:      Contains status information on the buffer itself.
+ * @data:      32-bit byte addressable pointer to the packet data.
+ */
+struct arc_emac_bd {
+       __le32 info;
+       dma_addr_t data;
+};
+
+/* Number of Rx/Tx BD's */
+#define RX_BD_NUM      128
+#define TX_BD_NUM      128
+
+#define RX_RING_SZ     (RX_BD_NUM * sizeof(struct arc_emac_bd))
+#define TX_RING_SZ     (TX_BD_NUM * sizeof(struct arc_emac_bd))
+
+/**
+ * struct buffer_state - Stores Rx/Tx buffer state.
+ * @sk_buff:   Pointer to socket buffer.
+ * @addr:      Start address of DMA-mapped memory region.
+ * @len:       Length of DMA-mapped memory region.
+ */
+struct buffer_state {
+       struct sk_buff *skb;
+       DEFINE_DMA_UNMAP_ADDR(addr);
+       DEFINE_DMA_UNMAP_LEN(len);
+};
+
+/**
+ * struct arc_emac_priv - Storage of EMAC's private information.
+ * @dev:       Pointer to the current device.
+ * @ndev:      Pointer to the current network device.
+ * @phy_dev:   Pointer to attached PHY device.
+ * @bus:       Pointer to the current MII bus.
+ * @regs:      Base address of EMAC memory-mapped control registers.
+ * @napi:      Structure for NAPI.
+ * @stats:     Network device statistics.
+ * @rxbd:      Pointer to Rx BD ring.
+ * @txbd:      Pointer to Tx BD ring.
+ * @rxbd_dma:  DMA handle for Rx BD ring.
+ * @txbd_dma:  DMA handle for Tx BD ring.
+ * @rx_buff:   Storage for Rx buffers states.
+ * @tx_buff:   Storage for Tx buffers states.
+ * @txbd_curr: Index of Tx BD to use on the next "ndo_start_xmit".
+ * @txbd_dirty:        Index of Tx BD to free on the next Tx interrupt.
+ * @last_rx_bd:        Index of the last Rx BD we've got from EMAC.
+ * @link:      PHY's last seen link state.
+ * @duplex:    PHY's last set duplex mode.
+ * @speed:     PHY's last set speed.
+ * @max_speed: Maximum supported by current system network data-rate.
+ */
+struct arc_emac_priv {
+       /* Devices */
+       struct device *dev;
+       struct net_device *ndev;
+       struct phy_device *phy_dev;
+       struct mii_bus *bus;
+
+       void __iomem *regs;
+
+       struct napi_struct napi;
+       struct net_device_stats stats;
+
+       struct arc_emac_bd *rxbd;
+       struct arc_emac_bd *txbd;
+
+       dma_addr_t rxbd_dma;
+       dma_addr_t txbd_dma;
+
+       struct buffer_state rx_buff[RX_BD_NUM];
+       struct buffer_state tx_buff[TX_BD_NUM];
+       unsigned int txbd_curr;
+       unsigned int txbd_dirty;
+
+       unsigned int last_rx_bd;
+
+       unsigned int link;
+       unsigned int duplex;
+       unsigned int speed;
+       unsigned int max_speed;
+};
+
+/**
+ * arc_reg_set - Sets EMAC register with provided value.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ * @value:     Value to set in register.
+ */
+static inline void arc_reg_set(struct arc_emac_priv *priv, int reg, int value)
+{
+       iowrite32(value, priv->regs + reg * sizeof(int));
+}
+
+/**
+ * arc_reg_get - Gets value of specified EMAC register.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ *
+ * returns:    Value of requested register.
+ */
+static inline unsigned int arc_reg_get(struct arc_emac_priv *priv, int reg)
+{
+       return ioread32(priv->regs + reg * sizeof(int));
+}
+
+/**
+ * arc_reg_or - Applies mask to specified EMAC register - ("reg" | "mask").
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ * @mask:      Mask to apply to specified register.
+ *
+ * This function reads initial register value, then applies provided mask
+ * to it and then writes register back.
+ */
+static inline void arc_reg_or(struct arc_emac_priv *priv, int reg, int mask)
+{
+       unsigned int value = arc_reg_get(priv, reg);
+       arc_reg_set(priv, reg, value | mask);
+}
+
+/**
+ * arc_reg_clr - Applies mask to specified EMAC register - ("reg" & ~"mask").
+ * @priv:      Pointer to ARC EMAC private data structure.
+ * @reg:       Register offset from base address.
+ * @mask:      Mask to apply to specified register.
+ *
+ * This function reads initial register value, then applies provided mask
+ * to it and then writes register back.
+ */
+static inline void arc_reg_clr(struct arc_emac_priv *priv, int reg, int mask)
+{
+       unsigned int value = arc_reg_get(priv, reg);
+       arc_reg_set(priv, reg, value & ~mask);
+}
+
+int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv);
+int arc_mdio_remove(struct arc_emac_priv *priv);
+
+#endif /* ARC_EMAC_H */
diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
new file mode 100644 (file)
index 0000000..f1b121e
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.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.
+ *
+ * Driver for the ARC EMAC 10100 (hardware revision 5)
+ *
+ * Contributors:
+ *             Amit Bhor
+ *             Sameer Dhavale
+ *             Vineet Gupta
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_platform.h>
+
+#include "emac.h"
+
+#define DRV_NAME       "arc_emac"
+#define DRV_VERSION    "1.0"
+
+/**
+ * arc_emac_adjust_link - Adjust the PHY link duplex.
+ * @ndev:      Pointer to the net_device structure.
+ *
+ * This function is called to change the duplex setting after auto negotiation
+ * is done by the PHY.
+ */
+static void arc_emac_adjust_link(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct phy_device *phy_dev = priv->phy_dev;
+       unsigned int reg, state_changed = 0;
+
+       if (priv->link != phy_dev->link) {
+               priv->link = phy_dev->link;
+               state_changed = 1;
+       }
+
+       if (priv->speed != phy_dev->speed) {
+               priv->speed = phy_dev->speed;
+               state_changed = 1;
+       }
+
+       if (priv->duplex != phy_dev->duplex) {
+               reg = arc_reg_get(priv, R_CTRL);
+
+               if (DUPLEX_FULL == phy_dev->duplex)
+                       reg |= ENFL_MASK;
+               else
+                       reg &= ~ENFL_MASK;
+
+               arc_reg_set(priv, R_CTRL, reg);
+               priv->duplex = phy_dev->duplex;
+               state_changed = 1;
+       }
+
+       if (state_changed)
+               phy_print_status(phy_dev);
+}
+
+/**
+ * arc_emac_get_settings - Get PHY settings.
+ * @ndev:      Pointer to net_device structure.
+ * @cmd:       Pointer to ethtool_cmd structure.
+ *
+ * This implements ethtool command for getting PHY settings. If PHY could
+ * not be found, the function returns -ENODEV. This function calls the
+ * relevant PHY ethtool API to get the PHY settings.
+ * Issue "ethtool ethX" under linux prompt to execute this function.
+ */
+static int arc_emac_get_settings(struct net_device *ndev,
+                                struct ethtool_cmd *cmd)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       return phy_ethtool_gset(priv->phy_dev, cmd);
+}
+
+/**
+ * arc_emac_set_settings - Set PHY settings as passed in the argument.
+ * @ndev:      Pointer to net_device structure.
+ * @cmd:       Pointer to ethtool_cmd structure.
+ *
+ * This implements ethtool command for setting various PHY settings. If PHY
+ * could not be found, the function returns -ENODEV. This function calls the
+ * relevant PHY ethtool API to set the PHY.
+ * Issue e.g. "ethtool -s ethX speed 1000" under linux prompt to execute this
+ * function.
+ */
+static int arc_emac_set_settings(struct net_device *ndev,
+                                struct ethtool_cmd *cmd)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       return phy_ethtool_sset(priv->phy_dev, cmd);
+}
+
+/**
+ * arc_emac_get_drvinfo - Get EMAC driver information.
+ * @ndev:      Pointer to net_device structure.
+ * @info:      Pointer to ethtool_drvinfo structure.
+ *
+ * This implements ethtool command for getting the driver information.
+ * Issue "ethtool -i ethX" under linux prompt to execute this function.
+ */
+static void arc_emac_get_drvinfo(struct net_device *ndev,
+                                struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+       strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+}
+
+static const struct ethtool_ops arc_emac_ethtool_ops = {
+       .get_settings   = arc_emac_get_settings,
+       .set_settings   = arc_emac_set_settings,
+       .get_drvinfo    = arc_emac_get_drvinfo,
+       .get_link       = ethtool_op_get_link,
+};
+
+#define FIRST_OR_LAST_MASK     (FIRST_MASK | LAST_MASK)
+
+/**
+ * arc_emac_tx_clean - clears processed by EMAC Tx BDs.
+ * @ndev:      Pointer to the network device.
+ */
+static void arc_emac_tx_clean(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &priv->stats;
+       unsigned int i;
+
+       for (i = 0; i < TX_BD_NUM; i++) {
+               unsigned int *txbd_dirty = &priv->txbd_dirty;
+               struct arc_emac_bd *txbd = &priv->txbd[*txbd_dirty];
+               struct buffer_state *tx_buff = &priv->tx_buff[*txbd_dirty];
+               struct sk_buff *skb = tx_buff->skb;
+               unsigned int info = le32_to_cpu(txbd->info);
+
+               *txbd_dirty = (*txbd_dirty + 1) % TX_BD_NUM;
+
+               if ((info & FOR_EMAC) || !txbd->data)
+                       break;
+
+               if (unlikely(info & (DROP | DEFR | LTCL | UFLO))) {
+                       stats->tx_errors++;
+                       stats->tx_dropped++;
+
+                       if (info & DEFR)
+                               stats->tx_carrier_errors++;
+
+                       if (info & LTCL)
+                               stats->collisions++;
+
+                       if (info & UFLO)
+                               stats->tx_fifo_errors++;
+               } else if (likely(info & FIRST_OR_LAST_MASK)) {
+                       stats->tx_packets++;
+                       stats->tx_bytes += skb->len;
+               }
+
+               dma_unmap_single(&ndev->dev, dma_unmap_addr(tx_buff, addr),
+                                dma_unmap_len(tx_buff, len), DMA_TO_DEVICE);
+
+               /* return the sk_buff to system */
+               dev_kfree_skb_irq(skb);
+
+               txbd->data = 0;
+               txbd->info = 0;
+
+               if (netif_queue_stopped(ndev))
+                       netif_wake_queue(ndev);
+       }
+}
+
+/**
+ * arc_emac_rx - processing of Rx packets.
+ * @ndev:      Pointer to the network device.
+ * @budget:    How many BDs to process on 1 call.
+ *
+ * returns:    Number of processed BDs
+ *
+ * Iterate through Rx BDs and deliver received packages to upper layer.
+ */
+static int arc_emac_rx(struct net_device *ndev, int budget)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       unsigned int work_done;
+
+       for (work_done = 0; work_done <= budget; work_done++) {
+               unsigned int *last_rx_bd = &priv->last_rx_bd;
+               struct net_device_stats *stats = &priv->stats;
+               struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd];
+               struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd];
+               unsigned int pktlen, info = le32_to_cpu(rxbd->info);
+               struct sk_buff *skb;
+               dma_addr_t addr;
+
+               if (unlikely((info & OWN_MASK) == FOR_EMAC))
+                       break;
+
+               /* Make a note that we saw a packet at this BD.
+                * So next time, driver starts from this + 1
+                */
+               *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM;
+
+               if (unlikely((info & FIRST_OR_LAST_MASK) !=
+                            FIRST_OR_LAST_MASK)) {
+                       /* We pre-allocate buffers of MTU size so incoming
+                        * packets won't be split/chained.
+                        */
+                       if (net_ratelimit())
+                               netdev_err(ndev, "incomplete packet received\n");
+
+                       /* Return ownership to EMAC */
+                       rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
+                       stats->rx_errors++;
+                       stats->rx_length_errors++;
+                       continue;
+               }
+
+               pktlen = info & LEN_MASK;
+               stats->rx_packets++;
+               stats->rx_bytes += pktlen;
+               skb = rx_buff->skb;
+               skb_put(skb, pktlen);
+               skb->dev = ndev;
+               skb->protocol = eth_type_trans(skb, ndev);
+
+               dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr),
+                                dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE);
+
+               /* Prepare the BD for next cycle */
+               rx_buff->skb = netdev_alloc_skb_ip_align(ndev,
+                                                        EMAC_BUFFER_SIZE);
+               if (unlikely(!rx_buff->skb)) {
+                       stats->rx_errors++;
+                       /* Because receive_skb is below, increment rx_dropped */
+                       stats->rx_dropped++;
+                       continue;
+               }
+
+               /* receive_skb only if new skb was allocated to avoid holes */
+               netif_receive_skb(skb);
+
+               addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data,
+                                     EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);
+               if (dma_mapping_error(&ndev->dev, addr)) {
+                       if (net_ratelimit())
+                               netdev_err(ndev, "cannot dma map\n");
+                       dev_kfree_skb(rx_buff->skb);
+                       stats->rx_errors++;
+                       continue;
+               }
+               dma_unmap_addr_set(rx_buff, addr, addr);
+               dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE);
+
+               rxbd->data = cpu_to_le32(addr);
+
+               /* Make sure pointer to data buffer is set */
+               wmb();
+
+               /* Return ownership to EMAC */
+               rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
+       }
+
+       return work_done;
+}
+
+/**
+ * arc_emac_poll - NAPI poll handler.
+ * @napi:      Pointer to napi_struct structure.
+ * @budget:    How many BDs to process on 1 call.
+ *
+ * returns:    Number of processed BDs
+ */
+static int arc_emac_poll(struct napi_struct *napi, int budget)
+{
+       struct net_device *ndev = napi->dev;
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       unsigned int work_done;
+
+       arc_emac_tx_clean(ndev);
+
+       work_done = arc_emac_rx(ndev, budget);
+       if (work_done < budget) {
+               napi_complete(napi);
+               arc_reg_or(priv, R_ENABLE, RXINT_MASK);
+       }
+
+       return work_done;
+}
+
+/**
+ * arc_emac_intr - Global interrupt handler for EMAC.
+ * @irq:               irq number.
+ * @dev_instance:      device instance.
+ *
+ * returns: IRQ_HANDLED for all cases.
+ *
+ * ARC EMAC has only 1 interrupt line, and depending on bits raised in
+ * STATUS register we may tell what is a reason for interrupt to fire.
+ */
+static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
+{
+       struct net_device *ndev = dev_instance;
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &priv->stats;
+       unsigned int status;
+
+       status = arc_reg_get(priv, R_STATUS);
+       status &= ~MDIO_MASK;
+
+       /* Reset all flags except "MDIO complete" */
+       arc_reg_set(priv, R_STATUS, status);
+
+       if (status & RXINT_MASK) {
+               if (likely(napi_schedule_prep(&priv->napi))) {
+                       arc_reg_clr(priv, R_ENABLE, RXINT_MASK);
+                       __napi_schedule(&priv->napi);
+               }
+       }
+
+       if (status & ERR_MASK) {
+               /* MSER/RXCR/RXFR/RXFL interrupt fires on corresponding
+                * 8-bit error counter overrun.
+                */
+
+               if (status & MSER_MASK) {
+                       stats->rx_missed_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+
+               if (status & RXCR_MASK) {
+                       stats->rx_crc_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+
+               if (status & RXFR_MASK) {
+                       stats->rx_frame_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+
+               if (status & RXFL_MASK) {
+                       stats->rx_over_errors += 0x100;
+                       stats->rx_errors += 0x100;
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * arc_emac_open - Open the network device.
+ * @ndev:      Pointer to the network device.
+ *
+ * returns: 0, on success or non-zero error value on failure.
+ *
+ * This function sets the MAC address, requests and enables an IRQ
+ * for the EMAC device and starts the Tx queue.
+ * It also connects to the phy device.
+ */
+static int arc_emac_open(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct phy_device *phy_dev = priv->phy_dev;
+       int i;
+
+       phy_dev->autoneg = AUTONEG_ENABLE;
+       phy_dev->speed = 0;
+       phy_dev->duplex = 0;
+       phy_dev->advertising = phy_dev->supported;
+
+       if (priv->max_speed > 100) {
+               phy_dev->advertising &= PHY_GBIT_FEATURES;
+       } else if (priv->max_speed <= 100) {
+               phy_dev->advertising &= PHY_BASIC_FEATURES;
+               if (priv->max_speed <= 10) {
+                       phy_dev->advertising &= ~SUPPORTED_100baseT_Half;
+                       phy_dev->advertising &= ~SUPPORTED_100baseT_Full;
+               }
+       }
+
+       priv->last_rx_bd = 0;
+
+       /* Allocate and set buffers for Rx BD's */
+       for (i = 0; i < RX_BD_NUM; i++) {
+               dma_addr_t addr;
+               unsigned int *last_rx_bd = &priv->last_rx_bd;
+               struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd];
+               struct buffer_state *rx_buff = &priv->rx_buff[*last_rx_bd];
+
+               rx_buff->skb = netdev_alloc_skb_ip_align(ndev,
+                                                        EMAC_BUFFER_SIZE);
+               if (unlikely(!rx_buff->skb))
+                       return -ENOMEM;
+
+               addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data,
+                                     EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);
+               if (dma_mapping_error(&ndev->dev, addr)) {
+                       netdev_err(ndev, "cannot dma map\n");
+                       dev_kfree_skb(rx_buff->skb);
+                       return -ENOMEM;
+               }
+               dma_unmap_addr_set(rx_buff, addr, addr);
+               dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE);
+
+               rxbd->data = cpu_to_le32(addr);
+
+               /* Make sure pointer to data buffer is set */
+               wmb();
+
+               /* Return ownership to EMAC */
+               rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);
+
+               *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM;
+       }
+
+       /* Clean Tx BD's */
+       memset(priv->txbd, 0, TX_RING_SZ);
+
+       /* Initialize logical address filter */
+       arc_reg_set(priv, R_LAFL, 0);
+       arc_reg_set(priv, R_LAFH, 0);
+
+       /* Set BD ring pointers for device side */
+       arc_reg_set(priv, R_RX_RING, (unsigned int)priv->rxbd_dma);
+       arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd_dma);
+
+       /* Enable interrupts */
+       arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
+
+       /* Set CONTROL */
+       arc_reg_set(priv, R_CTRL,
+                    (RX_BD_NUM << 24) |        /* RX BD table length */
+                    (TX_BD_NUM << 16) |        /* TX BD table length */
+                    TXRN_MASK | RXRN_MASK);
+
+       napi_enable(&priv->napi);
+
+       /* Enable EMAC */
+       arc_reg_or(priv, R_CTRL, EN_MASK);
+
+       phy_start_aneg(priv->phy_dev);
+
+       netif_start_queue(ndev);
+
+       return 0;
+}
+
+/**
+ * arc_emac_stop - Close the network device.
+ * @ndev:      Pointer to the network device.
+ *
+ * This function stops the Tx queue, disables interrupts and frees the IRQ for
+ * the EMAC device.
+ * It also disconnects the PHY device associated with the EMAC device.
+ */
+static int arc_emac_stop(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       napi_disable(&priv->napi);
+       netif_stop_queue(ndev);
+
+       /* Disable interrupts */
+       arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
+
+       /* Disable EMAC */
+       arc_reg_clr(priv, R_CTRL, EN_MASK);
+
+       return 0;
+}
+
+/**
+ * arc_emac_stats - Get system network statistics.
+ * @ndev:      Pointer to net_device structure.
+ *
+ * Returns the address of the device statistics structure.
+ * Statistics are updated in interrupt handler.
+ */
+static struct net_device_stats *arc_emac_stats(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &priv->stats;
+       unsigned long miss, rxerr;
+       u8 rxcrc, rxfram, rxoflow;
+
+       rxerr = arc_reg_get(priv, R_RXERR);
+       miss = arc_reg_get(priv, R_MISS);
+
+       rxcrc = rxerr;
+       rxfram = rxerr >> 8;
+       rxoflow = rxerr >> 16;
+
+       stats->rx_errors += miss;
+       stats->rx_errors += rxcrc + rxfram + rxoflow;
+
+       stats->rx_over_errors += rxoflow;
+       stats->rx_frame_errors += rxfram;
+       stats->rx_crc_errors += rxcrc;
+       stats->rx_missed_errors += miss;
+
+       return stats;
+}
+
+/**
+ * arc_emac_tx - Starts the data transmission.
+ * @skb:       sk_buff pointer that contains data to be Transmitted.
+ * @ndev:      Pointer to net_device structure.
+ *
+ * returns: NETDEV_TX_OK, on success
+ *             NETDEV_TX_BUSY, if any of the descriptors are not free.
+ *
+ * This function is invoked from upper layers to initiate transmission.
+ */
+static int arc_emac_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       unsigned int len, *txbd_curr = &priv->txbd_curr;
+       struct net_device_stats *stats = &priv->stats;
+       __le32 *info = &priv->txbd[*txbd_curr].info;
+       dma_addr_t addr;
+
+       if (skb_padto(skb, ETH_ZLEN))
+               return NETDEV_TX_OK;
+
+       len = max_t(unsigned int, ETH_ZLEN, skb->len);
+
+       /* EMAC still holds this buffer in its possession.
+        * CPU must not modify this buffer descriptor
+        */
+       if (unlikely((le32_to_cpu(*info) & OWN_MASK) == FOR_EMAC)) {
+               netif_stop_queue(ndev);
+               return NETDEV_TX_BUSY;
+       }
+
+       addr = dma_map_single(&ndev->dev, (void *)skb->data, len,
+                             DMA_TO_DEVICE);
+
+       if (unlikely(dma_mapping_error(&ndev->dev, addr))) {
+               stats->tx_dropped++;
+               stats->tx_errors++;
+               dev_kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+       dma_unmap_addr_set(&priv->tx_buff[*txbd_curr], addr, addr);
+       dma_unmap_len_set(&priv->tx_buff[*txbd_curr], len, len);
+
+       priv->tx_buff[*txbd_curr].skb = skb;
+       priv->txbd[*txbd_curr].data = cpu_to_le32(addr);
+
+       /* Make sure pointer to data buffer is set */
+       wmb();
+
+       *info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | len);
+
+       /* Increment index to point to the next BD */
+       *txbd_curr = (*txbd_curr + 1) % TX_BD_NUM;
+
+       /* Get "info" of the next BD */
+       info = &priv->txbd[*txbd_curr].info;
+
+       /* Check if if Tx BD ring is full - next BD is still owned by EMAC */
+       if (unlikely((le32_to_cpu(*info) & OWN_MASK) == FOR_EMAC))
+               netif_stop_queue(ndev);
+
+       arc_reg_set(priv, R_STATUS, TXPL_MASK);
+
+       skb_tx_timestamp(skb);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * arc_emac_set_address - Set the MAC address for this device.
+ * @ndev:      Pointer to net_device structure.
+ * @p:         6 byte Address to be written as MAC address.
+ *
+ * This function copies the HW address from the sockaddr structure to the
+ * net_device structure and updates the address in HW.
+ *
+ * returns:    -EBUSY if the net device is busy or 0 if the address is set
+ *             successfully.
+ */
+static int arc_emac_set_address(struct net_device *ndev, void *p)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+       struct sockaddr *addr = p;
+       unsigned int addr_low, addr_hi;
+
+       if (netif_running(ndev))
+               return -EBUSY;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
+
+       addr_low = le32_to_cpu(*(__le32 *) &ndev->dev_addr[0]);
+       addr_hi = le16_to_cpu(*(__le16 *) &ndev->dev_addr[4]);
+
+       arc_reg_set(priv, R_ADDRL, addr_low);
+       arc_reg_set(priv, R_ADDRH, addr_hi);
+
+       return 0;
+}
+
+static const struct net_device_ops arc_emac_netdev_ops = {
+       .ndo_open               = arc_emac_open,
+       .ndo_stop               = arc_emac_stop,
+       .ndo_start_xmit         = arc_emac_tx,
+       .ndo_set_mac_address    = arc_emac_set_address,
+       .ndo_get_stats          = arc_emac_stats,
+};
+
+static int arc_emac_probe(struct platform_device *pdev)
+{
+       struct resource res_regs, res_irq;
+       struct device_node *phy_node;
+       struct arc_emac_priv *priv;
+       struct net_device *ndev;
+       const char *mac_addr;
+       unsigned int id, clock_frequency;
+       int err;
+
+       if (!pdev->dev.of_node)
+               return -ENODEV;
+
+       /* Get PHY from device tree */
+       phy_node = of_parse_phandle(pdev->dev.of_node, "phy", 0);
+       if (!phy_node) {
+               dev_err(&pdev->dev, "failed to retrieve phy description from device tree\n");
+               return -ENODEV;
+       }
+
+       /* Get EMAC registers base address from device tree */
+       err = of_address_to_resource(pdev->dev.of_node, 0, &res_regs);
+       if (err) {
+               dev_err(&pdev->dev, "failed to retrieve registers base from device tree\n");
+               return -ENODEV;
+       }
+
+       /* Get CPU clock frequency from device tree */
+       if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+                                &clock_frequency)) {
+               dev_err(&pdev->dev, "failed to retrieve <clock-frequency> from device tree\n");
+               return -EINVAL;
+       }
+
+       /* Get IRQ from device tree */
+       err = of_irq_to_resource(pdev->dev.of_node, 0, &res_irq);
+       if (!err) {
+               dev_err(&pdev->dev, "failed to retrieve <irq> value from device tree\n");
+               return -ENODEV;
+       }
+
+       ndev = alloc_etherdev(sizeof(struct arc_emac_priv));
+       if (!ndev)
+               return -ENOMEM;
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       ndev->netdev_ops = &arc_emac_netdev_ops;
+       ndev->ethtool_ops = &arc_emac_ethtool_ops;
+       ndev->watchdog_timeo = TX_TIMEOUT;
+       /* FIXME :: no multicast support yet */
+       ndev->flags &= ~IFF_MULTICAST;
+
+       priv = netdev_priv(ndev);
+       priv->dev = &pdev->dev;
+       priv->ndev = ndev;
+
+       priv->regs = devm_ioremap_resource(&pdev->dev, &res_regs);
+       if (IS_ERR(priv->regs)) {
+               err = PTR_ERR(priv->regs);
+               goto out;
+       }
+       dev_dbg(&pdev->dev, "Registers base address is 0x%p\n", priv->regs);
+
+       id = arc_reg_get(priv, R_ID);
+
+       /* Check for EMAC revision 5 or 7, magic number */
+       if (!(id == 0x0005fd02 || id == 0x0007fd02)) {
+               dev_err(&pdev->dev, "ARC EMAC not detected, id=0x%x\n", id);
+               err = -ENODEV;
+               goto out;
+       }
+       dev_info(&pdev->dev, "ARC EMAC detected with id: 0x%x\n", id);
+
+       /* Set poll rate so that it polls every 1 ms */
+       arc_reg_set(priv, R_POLLRATE, clock_frequency / 1000000);
+
+       /* Get max speed of operation from device tree */
+       if (of_property_read_u32(pdev->dev.of_node, "max-speed",
+                                &priv->max_speed)) {
+               dev_err(&pdev->dev, "failed to retrieve <max-speed> from device tree\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       ndev->irq = res_irq.start;
+       dev_info(&pdev->dev, "IRQ is %d\n", ndev->irq);
+
+       /* Register interrupt handler for device */
+       err = devm_request_irq(&pdev->dev, ndev->irq, arc_emac_intr, 0,
+                              ndev->name, ndev);
+       if (err) {
+               dev_err(&pdev->dev, "could not allocate IRQ\n");
+               goto out;
+       }
+
+       /* Get MAC address from device tree */
+       mac_addr = of_get_mac_address(pdev->dev.of_node);
+
+       if (!mac_addr || !is_valid_ether_addr(mac_addr))
+               eth_hw_addr_random(ndev);
+       else
+               memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+
+       dev_info(&pdev->dev, "MAC address is now %pM\n", ndev->dev_addr);
+
+       /* Do 1 allocation instead of 2 separate ones for Rx and Tx BD rings */
+       priv->rxbd = dmam_alloc_coherent(&pdev->dev, RX_RING_SZ + TX_RING_SZ,
+                                        &priv->rxbd_dma, GFP_KERNEL);
+
+       if (!priv->rxbd) {
+               dev_err(&pdev->dev, "failed to allocate data buffers\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       priv->txbd = priv->rxbd + RX_BD_NUM;
+
+       priv->txbd_dma = priv->rxbd_dma + RX_RING_SZ;
+       dev_dbg(&pdev->dev, "EMAC Device addr: Rx Ring [0x%x], Tx Ring[%x]\n",
+               (unsigned int)priv->rxbd_dma, (unsigned int)priv->txbd_dma);
+
+       err = arc_mdio_probe(pdev, priv);
+       if (err) {
+               dev_err(&pdev->dev, "failed to probe MII bus\n");
+               goto out;
+       }
+
+       priv->phy_dev = of_phy_connect(ndev, phy_node, arc_emac_adjust_link, 0,
+                                      PHY_INTERFACE_MODE_MII);
+       if (!priv->phy_dev) {
+               dev_err(&pdev->dev, "of_phy_connect() failed\n");
+               err = -ENODEV;
+               goto out;
+       }
+
+       dev_info(&pdev->dev, "connected to %s phy with id 0x%x\n",
+                priv->phy_dev->drv->name, priv->phy_dev->phy_id);
+
+       netif_napi_add(ndev, &priv->napi, arc_emac_poll, ARC_EMAC_NAPI_WEIGHT);
+
+       err = register_netdev(ndev);
+       if (err) {
+               netif_napi_del(&priv->napi);
+               dev_err(&pdev->dev, "failed to register network device\n");
+               goto out;
+       }
+
+       return 0;
+
+out:
+       free_netdev(ndev);
+       return err;
+}
+
+static int arc_emac_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       phy_disconnect(priv->phy_dev);
+       priv->phy_dev = NULL;
+       arc_mdio_remove(priv);
+       unregister_netdev(ndev);
+       netif_napi_del(&priv->napi);
+       free_netdev(ndev);
+
+       return 0;
+}
+
+static const struct of_device_id arc_emac_dt_ids[] = {
+       { .compatible = "snps,arc-emac" },
+       { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, arc_emac_dt_ids);
+
+static struct platform_driver arc_emac_driver = {
+       .probe = arc_emac_probe,
+       .remove = arc_emac_remove,
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table  = arc_emac_dt_ids,
+               },
+};
+
+module_platform_driver(arc_emac_driver);
+
+MODULE_AUTHOR("Alexey Brodkin <abrodkin@synopsys.com>");
+MODULE_DESCRIPTION("ARC EMAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/arc/emac_mdio.c b/drivers/net/ethernet/arc/emac_mdio.c
new file mode 100644 (file)
index 0000000..26ba242
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com)
+ *
+ * MDIO implementation for ARC EMAC
+ */
+
+#include <linux/delay.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
+
+#include "emac.h"
+
+/* Number of seconds we wait for "MDIO complete" flag to appear */
+#define ARC_MDIO_COMPLETE_POLL_COUNT   1
+
+/**
+ * arc_mdio_complete_wait - Waits until MDIO transaction is completed.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ *
+ * returns:    0 on success, -ETIMEDOUT on a timeout.
+ */
+static int arc_mdio_complete_wait(struct arc_emac_priv *priv)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARC_MDIO_COMPLETE_POLL_COUNT * 40; i++) {
+               unsigned int status = arc_reg_get(priv, R_STATUS);
+
+               status &= MDIO_MASK;
+
+               if (status) {
+                       /* Reset "MDIO complete" flag */
+                       arc_reg_set(priv, R_STATUS, status);
+                       return 0;
+               }
+
+               msleep(25);
+       }
+
+       return -ETIMEDOUT;
+}
+
+/**
+ * arc_mdio_read - MDIO interface read function.
+ * @bus:       Pointer to MII bus structure.
+ * @phy_addr:  Address of the PHY device.
+ * @reg_num:   PHY register to read.
+ *
+ * returns:    The register contents on success, -ETIMEDOUT on a timeout.
+ *
+ * Reads the contents of the requested register from the requested PHY
+ * address.
+ */
+static int arc_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
+{
+       struct arc_emac_priv *priv = bus->priv;
+       unsigned int value;
+       int error;
+
+       arc_reg_set(priv, R_MDIO,
+                   0x60020000 | (phy_addr << 23) | (reg_num << 18));
+
+       error = arc_mdio_complete_wait(priv);
+       if (error < 0)
+               return error;
+
+       value = arc_reg_get(priv, R_MDIO) & 0xffff;
+
+       dev_dbg(priv->dev, "arc_mdio_read(phy_addr=%i, reg_num=%x) = %x\n",
+               phy_addr, reg_num, value);
+
+       return value;
+}
+
+/**
+ * arc_mdio_write - MDIO interface write function.
+ * @bus:       Pointer to MII bus structure.
+ * @phy_addr:  Address of the PHY device.
+ * @reg_num:   PHY register to write to.
+ * @value:     Value to be written into the register.
+ *
+ * returns:    0 on success, -ETIMEDOUT on a timeout.
+ *
+ * Writes the value to the requested register.
+ */
+static int arc_mdio_write(struct mii_bus *bus, int phy_addr,
+                         int reg_num, u16 value)
+{
+       struct arc_emac_priv *priv = bus->priv;
+
+       dev_dbg(priv->dev,
+               "arc_mdio_write(phy_addr=%i, reg_num=%x, value=%x)\n",
+               phy_addr, reg_num, value);
+
+       arc_reg_set(priv, R_MDIO,
+                    0x50020000 | (phy_addr << 23) | (reg_num << 18) | value);
+
+       return arc_mdio_complete_wait(priv);
+}
+
+/**
+ * arc_mdio_probe - MDIO probe function.
+ * @pdev:      Pointer to platform device.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ *
+ * returns:    0 on success, -ENOMEM when mdiobus_alloc
+ * (to allocate memory for MII bus structure) fails.
+ *
+ * Sets up and registers the MDIO interface.
+ */
+int arc_mdio_probe(struct platform_device *pdev, struct arc_emac_priv *priv)
+{
+       struct mii_bus *bus;
+       int error;
+
+       bus = mdiobus_alloc();
+       if (!bus)
+               return -ENOMEM;
+
+       priv->bus = bus;
+       bus->priv = priv;
+       bus->parent = priv->dev;
+       bus->name = "Synopsys MII Bus",
+       bus->read = &arc_mdio_read;
+       bus->write = &arc_mdio_write;
+
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
+
+       error = of_mdiobus_register(bus, pdev->dev.of_node);
+       if (error) {
+               dev_err(priv->dev, "cannot register MDIO bus %s\n", bus->name);
+               mdiobus_free(bus);
+               return error;
+       }
+
+       return 0;
+}
+
+/**
+ * arc_mdio_remove - MDIO remove function.
+ * @priv:      Pointer to ARC EMAC private data structure.
+ *
+ * Unregisters the MDIO and frees any associate memory for MII bus.
+ */
+int arc_mdio_remove(struct arc_emac_priv *priv)
+{
+       mdiobus_unregister(priv->bus);
+       mdiobus_free(priv->bus);
+       priv->bus = NULL;
+
+       return 0;
+}
index edda716..dedbd76 100644 (file)
@@ -1330,6 +1330,7 @@ enum {
        BNX2X_SP_RTNL_AFEX_F_UPDATE,
        BNX2X_SP_RTNL_ENABLE_SRIOV,
        BNX2X_SP_RTNL_VFPF_MCAST,
+       BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
        BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
        BNX2X_SP_RTNL_HYPERVISOR_VLAN,
 };
@@ -1500,6 +1501,7 @@ struct bnx2x {
 #define USING_SINGLE_MSIX_FLAG         (1 << 20)
 #define BC_SUPPORTS_DCBX_MSG_NON_PMF   (1 << 21)
 #define IS_VF_FLAG                     (1 << 22)
+#define INTERRUPTS_ENABLED_FLAG                (1 << 23)
 
 #define BP_NOMCP(bp)                   ((bp)->flags & NO_MCP_FLAG)
 
index ca7f2bb..ec3aa1d 100644 (file)
@@ -1722,7 +1722,7 @@ static int bnx2x_req_irq(struct bnx2x *bp)
        return request_irq(irq, bnx2x_interrupt, flags, bp->dev->name, bp->dev);
 }
 
-int bnx2x_setup_irqs(struct bnx2x *bp)
+static int bnx2x_setup_irqs(struct bnx2x *bp)
 {
        int rc = 0;
        if (bp->flags & USING_MSIX_FLAG &&
@@ -2871,6 +2871,9 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
        bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
        smp_mb();
 
+       /* indicate to VFs that the PF is going down */
+       bnx2x_iov_channel_down(bp);
+
        if (CNIC_LOADED(bp))
                bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
 
@@ -3540,9 +3543,12 @@ static void bnx2x_update_pbds_gso_enc(struct sk_buff *skb,
        /* outer IP header info */
        if (xmit_type & XMIT_CSUM_V4) {
                struct iphdr *iph = ip_hdr(skb);
+               u16 csum = (__force u16)(~iph->check) -
+                          (__force u16)iph->tot_len -
+                          (__force u16)iph->frag_off;
+
                pbd2->fw_ip_csum_wo_len_flags_frag =
-                       bswab16(csum_fold((~iph->check) -
-                                         iph->tot_len - iph->frag_off));
+                       bswab16(csum_fold((__force __wsum)csum));
        } else {
                pbd2->fw_ip_hdr_to_payload_w =
                        hlen_w - ((sizeof(struct ipv6hdr)) >> 1);
index 7c6faeb..b8c067d 100644 (file)
@@ -1391,7 +1391,7 @@ static bool bnx2x_is_nvm_accessible(struct bnx2x *bp)
                                          bp->pm_cap + PCI_PM_CTRL, &pm);
 
        if ((rc && !netif_running(dev)) ||
-           (!rc && ((pm & PCI_PM_CTRL_STATE_MASK) != PCI_D0)))
+           (!rc && ((pm & PCI_PM_CTRL_STATE_MASK) != (__force u16)PCI_D0)))
                return false;
 
        return true;
@@ -1610,8 +1610,10 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,
                 */
                val = be32_to_cpu(val_be);
 
-               val &= ~le32_to_cpu(0xff << BYTE_OFFSET(offset));
-               val |= le32_to_cpu(*data_buf << BYTE_OFFSET(offset));
+               val &= ~le32_to_cpu((__force __le32)
+                                   (0xff << BYTE_OFFSET(offset)));
+               val |= le32_to_cpu((__force __le32)
+                                  (*data_buf << BYTE_OFFSET(offset)));
 
                rc = bnx2x_nvram_write_dword(bp, align_offset, val,
                                             cmd_flags);
index c962d66..740518b 100644 (file)
@@ -615,7 +615,7 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
        if (rc) {
                BNX2X_ERR("DMAE returned failure %d\n", rc);
                bnx2x_panic();
-       };
+       }
 }
 
 static void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr,
@@ -5458,7 +5458,7 @@ static void bnx2x_timer(unsigned long data)
 
        /* sample pf vf bulletin board for new posts from pf */
        if (IS_VF(bp))
-               bnx2x_sample_bulletin(bp);
+               bnx2x_timer_sriov(bp);
 
        mod_timer(&bp->timer, jiffies + bp->current_interval);
 }
@@ -9620,6 +9620,13 @@ sp_rtnl_not_reset:
                   "sending set mcast vf pf channel message from rtnl sp-task\n");
                bnx2x_vfpf_set_mcast(bp->dev);
        }
+       if (test_and_clear_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
+                              &bp->sp_rtnl_state)){
+               if (!test_bit(__LINK_STATE_NOCARRIER, &bp->dev->state)) {
+                       bnx2x_tx_disable(bp);
+                       BNX2X_ERR("PF indicated channel is not servicable anymore. This means this VF device is no longer operational\n");
+               }
+       }
 
        if (test_and_clear_bit(BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
                               &bp->sp_rtnl_state)) {
@@ -10541,6 +10548,10 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg)
                if (!(bp->link_params.speed_cap_mask[idx] &
                                        PORT_HW_CFG_SPEED_CAPABILITY_D0_10G))
                        bp->port.supported[idx] &= ~SUPPORTED_10000baseT_Full;
+
+               if (!(bp->link_params.speed_cap_mask[idx] &
+                                       PORT_HW_CFG_SPEED_CAPABILITY_D0_20G))
+                       bp->port.supported[idx] &= ~SUPPORTED_20000baseKR2_Full;
        }
 
        BNX2X_DEV_INFO("supported 0x%x 0x%x\n", bp->port.supported[0],
@@ -12814,6 +12825,8 @@ static void __bnx2x_remove(struct pci_dev *pdev,
                rtnl_unlock();
        }
 
+       bnx2x_iov_remove_one(bp);
+
        /* Power on: we can't let PCI layer write to us while we are in D3 */
        if (IS_PF(bp))
                bnx2x_set_power_state(bp, PCI_D0);
@@ -12828,8 +12841,6 @@ static void __bnx2x_remove(struct pci_dev *pdev,
        /* Make sure RESET task is not scheduled before continuing */
        cancel_delayed_work_sync(&bp->sp_rtnl_task);
 
-       bnx2x_iov_remove_one(bp);
-
        /* send message via vfpf channel to release the resources of this vf */
        if (IS_VF(bp))
                bnx2x_vfpf_release(bp);
index 8a556dd..95861ef 100644 (file)
@@ -1459,21 +1459,16 @@ static u8 bnx2x_vf_is_pcie_pending(struct bnx2x *bp, u8 abs_vfid)
        struct bnx2x_virtf *vf = bnx2x_vf_by_abs_fid(bp, abs_vfid);
 
        if (!vf)
-               goto unknown_dev;
+               return false;
 
        dev = pci_get_bus_and_slot(vf->bus, vf->devfn);
        if (dev)
                return bnx2x_is_pcie_pending(dev);
-
-unknown_dev:
        return false;
 }
 
 int bnx2x_vf_flr_clnup_epilog(struct bnx2x *bp, u8 abs_vfid)
 {
-       /* Wait 100ms */
-       msleep(100);
-
        /* Verify no pending pci transactions */
        if (bnx2x_vf_is_pcie_pending(bp, abs_vfid))
                BNX2X_ERR("PCIE Transactions still pending\n");
@@ -2176,6 +2171,9 @@ int bnx2x_iov_nic_init(struct bnx2x *bp)
 
        DP(BNX2X_MSG_IOV, "num of vfs: %d\n", (bp)->vfdb->sriov.nr_virtfn);
 
+       /* let FLR complete ... */
+       msleep(100);
+
        /* initialize vf database */
        for_each_vf(bp, vfid) {
                struct bnx2x_virtf *vf = BP_VF(bp, vfid);
@@ -2777,6 +2775,10 @@ int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf, dma_addr_t *sb_map)
                   vf->abs_vfid, vf->state);
                return -EINVAL;
        }
+
+       /* let FLR complete ... */
+       msleep(100);
+
        /* FLR cleanup epilogue */
        if (bnx2x_vf_flr_clnup_epilog(bp, vf->abs_vfid))
                return -EBUSY;
@@ -3085,6 +3087,11 @@ void bnx2x_disable_sriov(struct bnx2x *bp)
 static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx,
                               struct bnx2x_virtf *vf)
 {
+       if (bp->state != BNX2X_STATE_OPEN) {
+               BNX2X_ERR("vf ndo called though PF is down\n");
+               return -EINVAL;
+       }
+
        if (!IS_SRIOV(bp)) {
                BNX2X_ERR("vf ndo called though sriov is disabled\n");
                return -EINVAL;
@@ -3419,6 +3426,20 @@ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp)
        return PFVF_BULLETIN_UPDATED;
 }
 
+void bnx2x_timer_sriov(struct bnx2x *bp)
+{
+       bnx2x_sample_bulletin(bp);
+
+       /* if channel is down we need to self destruct */
+       if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) {
+               smp_mb__before_clear_bit();
+               set_bit(BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
+                       &bp->sp_rtnl_state);
+               smp_mb__after_clear_bit();
+               schedule_delayed_work(&bp->sp_rtnl_task, 0);
+       }
+}
+
 void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 {
        /* vf doorbells are embedded within the regview */
@@ -3469,3 +3490,23 @@ int bnx2x_open_epilog(struct bnx2x *bp)
 
        return 0;
 }
+
+void bnx2x_iov_channel_down(struct bnx2x *bp)
+{
+       int vf_idx;
+       struct pf_vf_bulletin_content *bulletin;
+
+       if (!IS_SRIOV(bp))
+               return;
+
+       for_each_vf(bp, vf_idx) {
+               /* locate this VFs bulletin board and update the channel down
+                * bit
+                */
+               bulletin = BP_VF_BULLETIN(bp, vf_idx);
+               bulletin->valid_bitmap |= 1 << CHANNEL_DOWN;
+
+               /* update vf bulletin board */
+               bnx2x_post_vf_bulletin(bp, vf_idx);
+       }
+}
index f08c604..d143a7c 100644 (file)
@@ -751,6 +751,7 @@ static inline int bnx2x_vf_ustorm_prods_offset(struct bnx2x *bp,
 }
 
 enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp);
+void bnx2x_timer_sriov(struct bnx2x *bp);
 void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp);
 int bnx2x_vf_pci_alloc(struct bnx2x *bp);
 int bnx2x_enable_sriov(struct bnx2x *bp);
@@ -761,6 +762,7 @@ static inline int bnx2x_vf_headroom(struct bnx2x *bp)
 }
 void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp);
 int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs);
+void bnx2x_iov_channel_down(struct bnx2x *bp);
 int bnx2x_open_epilog(struct bnx2x *bp);
 
 #else /* CONFIG_BNX2X_SRIOV */
@@ -808,6 +810,7 @@ static inline enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp
 {
        return PFVF_BULLETIN_UNCHANGED;
 }
+static inline void bnx2x_timer_sriov(struct bnx2x *bp) {}
 
 static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 {
@@ -817,6 +820,7 @@ static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
 static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; }
 static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {}
 static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; }
+static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {}
 static inline int bnx2x_open_epilog(struct bnx2x *bp) {return 0; }
 
 #endif /* CONFIG_BNX2X_SRIOV */
index 861809d..2088063 100644 (file)
@@ -113,7 +113,7 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
 {
        struct cstorm_vf_zone_data __iomem *zone_data =
                REG_ADDR(bp, PXP_VF_ADDR_CSDM_GLOBAL_START);
-       int tout = 600, interval = 100; /* wait for 60 seconds */
+       int tout = 100, interval = 100; /* wait for 10 seconds */
 
        if (*done) {
                BNX2X_ERR("done was non zero before message to pf was sent\n");
@@ -121,6 +121,16 @@ static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
                return -EINVAL;
        }
 
+       /* if PF indicated channel is down avoid sending message. Return success
+        * so calling flow can continue
+        */
+       bnx2x_sample_bulletin(bp);
+       if (bp->old_bulletin.valid_bitmap & 1 << CHANNEL_DOWN) {
+               DP(BNX2X_MSG_IOV, "detecting channel down. Aborting message\n");
+               *done = PFVF_STATUS_SUCCESS;
+               return 0;
+       }
+
        /* Write message address */
        writel(U64_LO(msg_mapping),
               &zone_data->non_trigger.vf_pf_channel.msg_addr_lo);
index 41708fa..f3ad174 100644 (file)
@@ -331,7 +331,10 @@ struct pf_vf_bulletin_content {
 #define VLAN_VALID             1       /* when set, the vf should not access
                                         * the vfpf channel
                                         */
-
+#define CHANNEL_DOWN           2       /* vfpf channel is disabled. VFs are not
+                                        * to attempt to send messages on the
+                                        * channel after this bit is set
+                                        */
        u8 mac[ETH_ALEN];
        u8 mac_padding[2];
 
index cd69ac7..2df48bb 100644 (file)
@@ -4364,7 +4364,7 @@ static int be_resume(struct pci_dev *pdev)
        if (status)
                return status;
 
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        /* tell fw we're ready to fire cmds */
@@ -4460,7 +4460,7 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev)
                return PCI_ERS_RESULT_DISCONNECT;
 
        pci_set_master(pdev);
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
 
        /* Check if card is ok and fw is ready */
index e3ed6c5..8362a03 100644 (file)
 #define BM_MIIGSK_CFGR_RMII            0x01
 #define BM_MIIGSK_CFGR_FRCONT_10M      0x40
 
+#define RMON_T_DROP            0x200 /* Count of frames not cntd correctly */
+#define RMON_T_PACKETS         0x204 /* RMON TX packet count */
+#define RMON_T_BC_PKT          0x208 /* RMON TX broadcast pkts */
+#define RMON_T_MC_PKT          0x20C /* RMON TX multicast pkts */
+#define RMON_T_CRC_ALIGN       0x210 /* RMON TX pkts with CRC align err */
+#define RMON_T_UNDERSIZE       0x214 /* RMON TX pkts < 64 bytes, good CRC */
+#define RMON_T_OVERSIZE                0x218 /* RMON TX pkts > MAX_FL bytes good CRC */
+#define RMON_T_FRAG            0x21C /* RMON TX pkts < 64 bytes, bad CRC */
+#define RMON_T_JAB             0x220 /* RMON TX pkts > MAX_FL bytes, bad CRC */
+#define RMON_T_COL             0x224 /* RMON TX collision count */
+#define RMON_T_P64             0x228 /* RMON TX 64 byte pkts */
+#define RMON_T_P65TO127                0x22C /* RMON TX 65 to 127 byte pkts */
+#define RMON_T_P128TO255       0x230 /* RMON TX 128 to 255 byte pkts */
+#define RMON_T_P256TO511       0x234 /* RMON TX 256 to 511 byte pkts */
+#define RMON_T_P512TO1023      0x238 /* RMON TX 512 to 1023 byte pkts */
+#define RMON_T_P1024TO2047     0x23C /* RMON TX 1024 to 2047 byte pkts */
+#define RMON_T_P_GTE2048       0x240 /* RMON TX pkts > 2048 bytes */
+#define RMON_T_OCTETS          0x244 /* RMON TX octets */
+#define IEEE_T_DROP            0x248 /* Count of frames not counted crtly */
+#define IEEE_T_FRAME_OK                0x24C /* Frames tx'd OK */
+#define IEEE_T_1COL            0x250 /* Frames tx'd with single collision */
+#define IEEE_T_MCOL            0x254 /* Frames tx'd with multiple collision */
+#define IEEE_T_DEF             0x258 /* Frames tx'd after deferral delay */
+#define IEEE_T_LCOL            0x25C /* Frames tx'd with late collision */
+#define IEEE_T_EXCOL           0x260 /* Frames tx'd with excesv collisions */
+#define IEEE_T_MACERR          0x264 /* Frames tx'd with TX FIFO underrun */
+#define IEEE_T_CSERR           0x268 /* Frames tx'd with carrier sense err */
+#define IEEE_T_SQE             0x26C /* Frames tx'd with SQE err */
+#define IEEE_T_FDXFC           0x270 /* Flow control pause frames tx'd */
+#define IEEE_T_OCTETS_OK       0x274 /* Octet count for frames tx'd w/o err */
+#define RMON_R_PACKETS         0x284 /* RMON RX packet count */
+#define RMON_R_BC_PKT          0x288 /* RMON RX broadcast pkts */
+#define RMON_R_MC_PKT          0x28C /* RMON RX multicast pkts */
+#define RMON_R_CRC_ALIGN       0x290 /* RMON RX pkts with CRC alignment err */
+#define RMON_R_UNDERSIZE       0x294 /* RMON RX pkts < 64 bytes, good CRC */
+#define RMON_R_OVERSIZE                0x298 /* RMON RX pkts > MAX_FL bytes good CRC */
+#define RMON_R_FRAG            0x29C /* RMON RX pkts < 64 bytes, bad CRC */
+#define RMON_R_JAB             0x2A0 /* RMON RX pkts > MAX_FL bytes, bad CRC */
+#define RMON_R_RESVD_O         0x2A4 /* Reserved */
+#define RMON_R_P64             0x2A8 /* RMON RX 64 byte pkts */
+#define RMON_R_P65TO127                0x2AC /* RMON RX 65 to 127 byte pkts */
+#define RMON_R_P128TO255       0x2B0 /* RMON RX 128 to 255 byte pkts */
+#define RMON_R_P256TO511       0x2B4 /* RMON RX 256 to 511 byte pkts */
+#define RMON_R_P512TO1023      0x2B8 /* RMON RX 512 to 1023 byte pkts */
+#define RMON_R_P1024TO2047     0x2BC /* RMON RX 1024 to 2047 byte pkts */
+#define RMON_R_P_GTE2048       0x2C0 /* RMON RX pkts > 2048 bytes */
+#define RMON_R_OCTETS          0x2C4 /* RMON RX octets */
+#define IEEE_R_DROP            0x2C8 /* Count frames not counted correctly */
+#define IEEE_R_FRAME_OK                0x2CC /* Frames rx'd OK */
+#define IEEE_R_CRC             0x2D0 /* Frames rx'd with CRC err */
+#define IEEE_R_ALIGN           0x2D4 /* Frames rx'd with alignment err */
+#define IEEE_R_MACERR          0x2D8 /* Receive FIFO overflow count */
+#define IEEE_R_FDXFC           0x2DC /* Flow control pause frames rx'd */
+#define IEEE_R_OCTETS_OK       0x2E0 /* Octet cnt for frames rx'd w/o err */
+
 #else
 
 #define FEC_ECNTRL             0x000 /* Ethernet control reg */
index 46f2544..ed6180e 100644 (file)
@@ -604,6 +604,14 @@ fec_restart(struct net_device *ndev, int duplex)
        if (fep->bufdesc_ex)
                ecntl |= (1 << 4);
 
+#ifndef CONFIG_M5272
+       /* Disable, clear, and enable the MIB */
+       writel(1 << 31, fep->hwp + FEC_MIB_CTRLSTAT);
+       for (i = RMON_T_DROP; i < IEEE_R_OCTETS_OK; i++)
+               writel(0, fep->hwp + i);
+       writel(0, fep->hwp + FEC_MIB_CTRLSTAT);
+#endif
+
        /* And last, enable the transmit and receive processing */
        writel(ecntl, fep->hwp + FEC_ECNTRL);
        writel(0, fep->hwp + FEC_R_DES_ACTIVE);
@@ -1435,6 +1443,107 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
        return 0;
 }
 
+#ifndef CONFIG_M5272
+static const struct fec_stat {
+       char name[ETH_GSTRING_LEN];
+       u16 offset;
+} fec_stats[] = {
+       /* RMON TX */
+       { "tx_dropped", RMON_T_DROP },
+       { "tx_packets", RMON_T_PACKETS },
+       { "tx_broadcast", RMON_T_BC_PKT },
+       { "tx_multicast", RMON_T_MC_PKT },
+       { "tx_crc_errors", RMON_T_CRC_ALIGN },
+       { "tx_undersize", RMON_T_UNDERSIZE },
+       { "tx_oversize", RMON_T_OVERSIZE },
+       { "tx_fragment", RMON_T_FRAG },
+       { "tx_jabber", RMON_T_JAB },
+       { "tx_collision", RMON_T_COL },
+       { "tx_64byte", RMON_T_P64 },
+       { "tx_65to127byte", RMON_T_P65TO127 },
+       { "tx_128to255byte", RMON_T_P128TO255 },
+       { "tx_256to511byte", RMON_T_P256TO511 },
+       { "tx_512to1023byte", RMON_T_P512TO1023 },
+       { "tx_1024to2047byte", RMON_T_P1024TO2047 },
+       { "tx_GTE2048byte", RMON_T_P_GTE2048 },
+       { "tx_octets", RMON_T_OCTETS },
+
+       /* IEEE TX */
+       { "IEEE_tx_drop", IEEE_T_DROP },
+       { "IEEE_tx_frame_ok", IEEE_T_FRAME_OK },
+       { "IEEE_tx_1col", IEEE_T_1COL },
+       { "IEEE_tx_mcol", IEEE_T_MCOL },
+       { "IEEE_tx_def", IEEE_T_DEF },
+       { "IEEE_tx_lcol", IEEE_T_LCOL },
+       { "IEEE_tx_excol", IEEE_T_EXCOL },
+       { "IEEE_tx_macerr", IEEE_T_MACERR },
+       { "IEEE_tx_cserr", IEEE_T_CSERR },
+       { "IEEE_tx_sqe", IEEE_T_SQE },
+       { "IEEE_tx_fdxfc", IEEE_T_FDXFC },
+       { "IEEE_tx_octets_ok", IEEE_T_OCTETS_OK },
+
+       /* RMON RX */
+       { "rx_packets", RMON_R_PACKETS },
+       { "rx_broadcast", RMON_R_BC_PKT },
+       { "rx_multicast", RMON_R_MC_PKT },
+       { "rx_crc_errors", RMON_R_CRC_ALIGN },
+       { "rx_undersize", RMON_R_UNDERSIZE },
+       { "rx_oversize", RMON_R_OVERSIZE },
+       { "rx_fragment", RMON_R_FRAG },
+       { "rx_jabber", RMON_R_JAB },
+       { "rx_64byte", RMON_R_P64 },
+       { "rx_65to127byte", RMON_R_P65TO127 },
+       { "rx_128to255byte", RMON_R_P128TO255 },
+       { "rx_256to511byte", RMON_R_P256TO511 },
+       { "rx_512to1023byte", RMON_R_P512TO1023 },
+       { "rx_1024to2047byte", RMON_R_P1024TO2047 },
+       { "rx_GTE2048byte", RMON_R_P_GTE2048 },
+       { "rx_octets", RMON_R_OCTETS },
+
+       /* IEEE RX */
+       { "IEEE_rx_drop", IEEE_R_DROP },
+       { "IEEE_rx_frame_ok", IEEE_R_FRAME_OK },
+       { "IEEE_rx_crc", IEEE_R_CRC },
+       { "IEEE_rx_align", IEEE_R_ALIGN },
+       { "IEEE_rx_macerr", IEEE_R_MACERR },
+       { "IEEE_rx_fdxfc", IEEE_R_FDXFC },
+       { "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK },
+};
+
+static void fec_enet_get_ethtool_stats(struct net_device *dev,
+       struct ethtool_stats *stats, u64 *data)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
+               data[i] = readl(fep->hwp + fec_stats[i].offset);
+}
+
+static void fec_enet_get_strings(struct net_device *netdev,
+       u32 stringset, u8 *data)
+{
+       int i;
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                               fec_stats[i].name, ETH_GSTRING_LEN);
+               break;
+       }
+}
+
+static int fec_enet_get_sset_count(struct net_device *dev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(fec_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+#endif
+
 static int fec_enet_nway_reset(struct net_device *dev)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
@@ -1455,6 +1564,11 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_ts_info            = fec_enet_get_ts_info,
        .nway_reset             = fec_enet_nway_reset,
+#ifndef CONFIG_M5272
+       .get_ethtool_stats      = fec_enet_get_ethtool_stats,
+       .get_strings            = fec_enet_get_strings,
+       .get_sset_count         = fec_enet_get_sset_count,
+#endif
 };
 
 static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
index d2bea3f..5115ae7 100644 (file)
@@ -3069,7 +3069,7 @@ static int e100_resume(struct pci_dev *pdev)
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
        /* ack any pending wake events, disable PME */
-       pci_enable_wake(pdev, 0, 0);
+       pci_enable_wake(pdev, PCI_D0, 0);
 
        /* disable reverse auto-negotiation */
        if (nic->phy == phy_82552_v) {
@@ -3160,7 +3160,7 @@ static void e100_io_resume(struct pci_dev *pdev)
        struct nic *nic = netdev_priv(netdev);
 
        /* ack any pending wake events, disable PME */
-       pci_enable_wake(pdev, 0, 0);
+       pci_enable_wake(pdev, PCI_D0, 0);
 
        netif_device_attach(netdev);
        if (netif_running(netdev)) {
index 64646eb..270e65f 100644 (file)
@@ -483,7 +483,6 @@ static void korina_multicast_list(struct net_device *dev)
        unsigned long flags;
        struct netdev_hw_addr *ha;
        u32 recognise = ETH_ARC_AB;     /* always accept broadcasts */
-       int i;
 
        /* Set promiscuous mode */
        if (dev->flags & IFF_PROMISC)
index ea1e038..df04c82 100644 (file)
@@ -257,6 +257,8 @@ static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op,
 
        if (!wait_for_completion_timeout(&context->done,
                                         msecs_to_jiffies(timeout))) {
+               mlx4_warn(dev, "communication channel command 0x%x timed out\n",
+                         op);
                err = -EBUSY;
                goto out;
        }
@@ -486,6 +488,8 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
        }
 
        if (cmd_pending(dev)) {
+               mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n",
+                         op);
                err = -ETIMEDOUT;
                goto out;
        }
@@ -549,6 +553,8 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
 
        if (!wait_for_completion_timeout(&context->done,
                                         msecs_to_jiffies(timeout))) {
+               mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n",
+                         op);
                err = -EBUSY;
                goto out;
        }
index 0f91222..9d4a1ea 100644 (file)
@@ -207,9 +207,6 @@ static int mlx4_en_dcbnl_ieee_getmaxrate(struct net_device *dev,
        struct mlx4_en_priv *priv = netdev_priv(dev);
        int i;
 
-       if (!priv->maxrate)
-               return -EINVAL;
-
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
                maxrate->tc_maxrate[i] =
                        priv->maxrate[i] * MLX4_RATELIMIT_UNITS_IN_KB;
index a5c9df0..a071cda 100644 (file)
@@ -310,7 +310,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
 err_mr:
        (void) mlx4_mr_free(dev, &mdev->mr);
 err_map:
-       if (!mdev->uar_map)
+       if (mdev->uar_map)
                iounmap(mdev->uar_map);
 err_uar:
        mlx4_uar_free(dev, &mdev->priv_uar);
index 7299ada..caf2047 100644 (file)
@@ -405,7 +405,7 @@ static int mlx4_en_vlan_rx_add_vid(struct net_device *dev,
                        en_err(priv, "Failed configuring VLAN filter\n");
        }
        if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx))
-               en_err(priv, "failed adding vlan %d\n", vid);
+               en_dbg(HW, priv, "failed adding vlan %d\n", vid);
        mutex_unlock(&mdev->state_lock);
 
        return 0;
@@ -428,7 +428,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
        if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx))
                mlx4_unregister_vlan(mdev->dev, priv->port, idx);
        else
-               en_err(priv, "could not find vid %d in cache\n", vid);
+               en_dbg(HW, priv, "could not find vid %d in cache\n", vid);
 
        if (mdev->device_up && priv->port_up) {
                err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
@@ -1236,10 +1236,19 @@ static void mlx4_en_tx_timeout(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       int i;
 
        if (netif_msg_timer(priv))
                en_warn(priv, "Tx timeout called on port:%d\n", priv->port);
 
+       for (i = 0; i < priv->tx_ring_num; i++) {
+               if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i)))
+                       continue;
+               en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n",
+                       i, priv->tx_ring[i].qpn, priv->tx_ring[i].cqn,
+                       priv->tx_ring[i].cons, priv->tx_ring[i].prod);
+       }
+
        priv->port_stats.tx_timeout++;
        en_dbg(DRV, priv, "Scheduling watchdog\n");
        queue_work(mdev->workqueue, &priv->watchdog_task);
@@ -1375,12 +1384,13 @@ static void mlx4_en_do_get_stats(struct work_struct *work)
 
        mutex_lock(&mdev->state_lock);
        if (mdev->device_up) {
-               err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0);
-               if (err)
-                       en_dbg(HW, priv, "Could not update stats\n");
+               if (priv->port_up) {
+                       err = mlx4_en_DUMP_ETH_STATS(mdev, priv->port, 0);
+                       if (err)
+                               en_dbg(HW, priv, "Could not update stats\n");
 
-               if (priv->port_up)
                        mlx4_en_auto_moderation(priv);
+               }
 
                queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
        }
@@ -1634,6 +1644,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
                return;
        }
 
+       /* close port*/
+       mlx4_CLOSE_PORT(mdev->dev, priv->port);
+
        /* Synchronize with tx routine */
        netif_tx_lock_bh(dev);
        if (detach)
@@ -1734,14 +1747,11 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
                }
                local_bh_enable();
 
-               mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]);
                while (test_bit(NAPI_STATE_SCHED, &cq->napi.state))
                        msleep(1);
+               mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]);
                mlx4_en_deactivate_cq(priv, cq);
        }
-
-       /* close port*/
-       mlx4_CLOSE_PORT(mdev->dev, priv->port);
 }
 
 static void mlx4_en_restart(struct work_struct *work)
@@ -2322,6 +2332,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        mdev->pndev[port] = dev;
 
        netif_carrier_off(dev);
+       mlx4_en_set_default_moderation(priv);
+
        err = register_netdev(dev);
        if (err) {
                en_err(priv, "Netdev registration failed for port %d\n", port);
@@ -2353,7 +2365,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
                en_err(priv, "Failed Initializing port\n");
                goto out;
        }
-       mlx4_en_set_default_moderation(priv);
        queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
 
        if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
index 9c57581..76997b9 100644 (file)
 
 #include "mlx4_en.h"
 
+static int mlx4_alloc_pages(struct mlx4_en_priv *priv,
+                           struct mlx4_en_rx_alloc *page_alloc,
+                           const struct mlx4_en_frag_info *frag_info,
+                           gfp_t _gfp)
+{
+       int order;
+       struct page *page;
+       dma_addr_t dma;
+
+       for (order = MLX4_EN_ALLOC_PREFER_ORDER; ;) {
+               gfp_t gfp = _gfp;
+
+               if (order)
+                       gfp |= __GFP_COMP | __GFP_NOWARN;
+               page = alloc_pages(gfp, order);
+               if (likely(page))
+                       break;
+               if (--order < 0 ||
+                   ((PAGE_SIZE << order) < frag_info->frag_size))
+                       return -ENOMEM;
+       }
+       dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE << order,
+                          PCI_DMA_FROMDEVICE);
+       if (dma_mapping_error(priv->ddev, dma)) {
+               put_page(page);
+               return -ENOMEM;
+       }
+       page_alloc->size = PAGE_SIZE << order;
+       page_alloc->page = page;
+       page_alloc->dma = dma;
+       page_alloc->offset = frag_info->frag_align;
+       /* Not doing get_page() for each frag is a big win
+        * on asymetric workloads.
+        */
+       atomic_set(&page->_count, page_alloc->size / frag_info->frag_stride);
+       return 0;
+}
+
 static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv,
                               struct mlx4_en_rx_desc *rx_desc,
                               struct mlx4_en_rx_alloc *frags,
-                              struct mlx4_en_rx_alloc *ring_alloc)
+                              struct mlx4_en_rx_alloc *ring_alloc,
+                              gfp_t gfp)
 {
        struct mlx4_en_rx_alloc page_alloc[MLX4_EN_MAX_RX_FRAGS];
-       struct mlx4_en_frag_info *frag_info;
+       const struct mlx4_en_frag_info *frag_info;
        struct page *page;
        dma_addr_t dma;
        int i;
 
        for (i = 0; i < priv->num_frags; i++) {
                frag_info = &priv->frag_info[i];
-               if (ring_alloc[i].offset == frag_info->last_offset) {
-                       page = alloc_pages(GFP_ATOMIC | __GFP_COMP,
-                                       MLX4_EN_ALLOC_ORDER);
-                       if (!page)
-                               goto out;
-                       dma = dma_map_page(priv->ddev, page, 0,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-                       if (dma_mapping_error(priv->ddev, dma)) {
-                               put_page(page);
-                               goto out;
-                       }
-                       page_alloc[i].page = page;
-                       page_alloc[i].dma = dma;
-                       page_alloc[i].offset = frag_info->frag_align;
-               } else {
-                       page_alloc[i].page = ring_alloc[i].page;
-                       get_page(ring_alloc[i].page);
-                       page_alloc[i].dma = ring_alloc[i].dma;
-                       page_alloc[i].offset = ring_alloc[i].offset +
-                                               frag_info->frag_stride;
-               }
+               page_alloc[i] = ring_alloc[i];
+               page_alloc[i].offset += frag_info->frag_stride;
+               if (page_alloc[i].offset + frag_info->frag_stride <= ring_alloc[i].size)
+                       continue;
+               if (mlx4_alloc_pages(priv, &page_alloc[i], frag_info, gfp))
+                       goto out;
        }
 
        for (i = 0; i < priv->num_frags; i++) {
@@ -88,14 +112,16 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv,
 
        return 0;
 
-
 out:
        while (i--) {
                frag_info = &priv->frag_info[i];
-               if (ring_alloc[i].offset == frag_info->last_offset)
+               if (page_alloc[i].page != ring_alloc[i].page) {
                        dma_unmap_page(priv->ddev, page_alloc[i].dma,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               put_page(page_alloc[i].page);
+                               page_alloc[i].size, PCI_DMA_FROMDEVICE);
+                       page = page_alloc[i].page;
+                       atomic_set(&page->_count, 1);
+                       put_page(page);
+               }
        }
        return -ENOMEM;
 }
@@ -104,12 +130,12 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
                              struct mlx4_en_rx_alloc *frags,
                              int i)
 {
-       struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
+       const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
 
-       if (frags[i].offset == frag_info->last_offset) {
-               dma_unmap_page(priv->ddev, frags[i].dma, MLX4_EN_ALLOC_SIZE,
+       if (frags[i].offset + frag_info->frag_stride > frags[i].size)
+               dma_unmap_page(priv->ddev, frags[i].dma, frags[i].size,
                                         PCI_DMA_FROMDEVICE);
-       }
+
        if (frags[i].page)
                put_page(frags[i].page);
 }
@@ -117,35 +143,28 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv,
 static int mlx4_en_init_allocator(struct mlx4_en_priv *priv,
                                  struct mlx4_en_rx_ring *ring)
 {
-       struct mlx4_en_rx_alloc *page_alloc;
        int i;
+       struct mlx4_en_rx_alloc *page_alloc;
 
        for (i = 0; i < priv->num_frags; i++) {
-               page_alloc = &ring->page_alloc[i];
-               page_alloc->page = alloc_pages(GFP_ATOMIC | __GFP_COMP,
-                                              MLX4_EN_ALLOC_ORDER);
-               if (!page_alloc->page)
-                       goto out;
+               const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
 
-               page_alloc->dma = dma_map_page(priv->ddev, page_alloc->page, 0,
-                                       MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               if (dma_mapping_error(priv->ddev, page_alloc->dma)) {
-                       put_page(page_alloc->page);
-                       page_alloc->page = NULL;
+               if (mlx4_alloc_pages(priv, &ring->page_alloc[i],
+                                    frag_info, GFP_KERNEL))
                        goto out;
-               }
-               page_alloc->offset = priv->frag_info[i].frag_align;
-               en_dbg(DRV, priv, "Initialized allocator:%d with page:%p\n",
-                      i, page_alloc->page);
        }
        return 0;
 
 out:
        while (i--) {
+               struct page *page;
+
                page_alloc = &ring->page_alloc[i];
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               put_page(page_alloc->page);
+                              page_alloc->size, PCI_DMA_FROMDEVICE);
+               page = page_alloc->page;
+               atomic_set(&page->_count, 1);
+               put_page(page);
                page_alloc->page = NULL;
        }
        return -ENOMEM;
@@ -158,13 +177,18 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv,
        int i;
 
        for (i = 0; i < priv->num_frags; i++) {
+               const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i];
+
                page_alloc = &ring->page_alloc[i];
                en_dbg(DRV, priv, "Freeing allocator:%d count:%d\n",
                       i, page_count(page_alloc->page));
 
                dma_unmap_page(priv->ddev, page_alloc->dma,
-                               MLX4_EN_ALLOC_SIZE, PCI_DMA_FROMDEVICE);
-               put_page(page_alloc->page);
+                               page_alloc->size, PCI_DMA_FROMDEVICE);
+               while (page_alloc->offset + frag_info->frag_stride < page_alloc->size) {
+                       put_page(page_alloc->page);
+                       page_alloc->offset += frag_info->frag_stride;
+               }
                page_alloc->page = NULL;
        }
 }
@@ -195,13 +219,14 @@ static void mlx4_en_init_rx_desc(struct mlx4_en_priv *priv,
 }
 
 static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv,
-                                  struct mlx4_en_rx_ring *ring, int index)
+                                  struct mlx4_en_rx_ring *ring, int index,
+                                  gfp_t gfp)
 {
        struct mlx4_en_rx_desc *rx_desc = ring->buf + (index * ring->stride);
        struct mlx4_en_rx_alloc *frags = ring->rx_info +
                                        (index << priv->log_rx_info);
 
-       return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc);
+       return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc, gfp);
 }
 
 static inline void mlx4_en_update_rx_prod_db(struct mlx4_en_rx_ring *ring)
@@ -235,7 +260,8 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv)
                        ring = &priv->rx_ring[ring_ind];
 
                        if (mlx4_en_prepare_rx_desc(priv, ring,
-                                                   ring->actual_size)) {
+                                                   ring->actual_size,
+                                                   GFP_KERNEL)) {
                                if (ring->actual_size < MLX4_EN_MIN_RX_SIZE) {
                                        en_err(priv, "Failed to allocate "
                                                     "enough rx buffers\n");
@@ -450,11 +476,11 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
                                        DMA_FROM_DEVICE);
 
                /* Save page reference in skb */
-               get_page(frags[nr].page);
                __skb_frag_set_page(&skb_frags_rx[nr], frags[nr].page);
                skb_frag_size_set(&skb_frags_rx[nr], frag_info->frag_size);
                skb_frags_rx[nr].page_offset = frags[nr].offset;
                skb->truesize += frag_info->frag_stride;
+               frags[nr].page = NULL;
        }
        /* Adjust size of last fragment to match actual length */
        if (nr > 0)
@@ -547,7 +573,7 @@ static void mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv,
        int index = ring->prod & ring->size_mask;
 
        while ((u32) (ring->prod - ring->cons) < ring->actual_size) {
-               if (mlx4_en_prepare_rx_desc(priv, ring, index))
+               if (mlx4_en_prepare_rx_desc(priv, ring, index, GFP_ATOMIC))
                        break;
                ring->prod++;
                index = ring->prod & ring->size_mask;
@@ -805,21 +831,7 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
        return done;
 }
 
-
-/* Calculate the last offset position that accommodates a full fragment
- * (assuming fagment size = stride-align) */
-static int mlx4_en_last_alloc_offset(struct mlx4_en_priv *priv, u16 stride, u16 align)
-{
-       u16 res = MLX4_EN_ALLOC_SIZE % stride;
-       u16 offset = MLX4_EN_ALLOC_SIZE - stride - res + align;
-
-       en_dbg(DRV, priv, "Calculated last offset for stride:%d align:%d "
-                           "res:%d offset:%d\n", stride, align, res, offset);
-       return offset;
-}
-
-
-static int frag_sizes[] = {
+static const int frag_sizes[] = {
        FRAG_SZ0,
        FRAG_SZ1,
        FRAG_SZ2,
@@ -847,9 +859,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev)
                        priv->frag_info[i].frag_stride =
                                ALIGN(frag_sizes[i], SMP_CACHE_BYTES);
                }
-               priv->frag_info[i].last_offset = mlx4_en_last_alloc_offset(
-                                               priv, priv->frag_info[i].frag_stride,
-                                               priv->frag_info[i].frag_align);
                buf_size += priv->frag_info[i].frag_size;
                i++;
        }
@@ -861,13 +870,13 @@ void mlx4_en_calc_rx_buf(struct net_device *dev)
        en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d "
                  "num_frags:%d):\n", eff_mtu, priv->num_frags);
        for (i = 0; i < priv->num_frags; i++) {
-               en_dbg(DRV, priv, "  frag:%d - size:%d prefix:%d align:%d "
-                               "stride:%d last_offset:%d\n", i,
-                               priv->frag_info[i].frag_size,
-                               priv->frag_info[i].frag_prefix_size,
-                               priv->frag_info[i].frag_align,
-                               priv->frag_info[i].frag_stride,
-                               priv->frag_info[i].last_offset);
+               en_err(priv,
+                      "  frag:%d - size:%d prefix:%d align:%d stride:%d\n",
+                      i,
+                      priv->frag_info[i].frag_size,
+                      priv->frag_info[i].frag_prefix_size,
+                      priv->frag_info[i].frag_align,
+                      priv->frag_info[i].frag_stride);
        }
 }
 
index 2f4a260..56160a2 100644 (file)
@@ -839,11 +839,11 @@ static ssize_t set_port_ib_mtu(struct device *dev,
                return -EINVAL;
        }
 
-       err = sscanf(buf, "%d", &mtu);
-       if (err > 0)
+       err = kstrtoint(buf, 0, &mtu);
+       if (!err)
                ibta_mtu = int_to_ibta_mtu(mtu);
 
-       if (err <= 0 || ibta_mtu < 0) {
+       if (err || ibta_mtu < 0) {
                mlx4_err(mdev, "%s is invalid IBTA mtu\n", buf);
                return -EINVAL;
        }
@@ -2077,6 +2077,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                       num_vfs, MLX4_MAX_NUM_VF);
                return -EINVAL;
        }
+
+       if (num_vfs < 0) {
+               pr_err("num_vfs module parameter cannot be negative\n");
+               return -EINVAL;
+       }
        /*
         * Check for BARs.
         */
index 57192a8..35fb60e 100644 (file)
@@ -96,7 +96,8 @@
 
 /* Use the maximum between 16384 and a single page */
 #define MLX4_EN_ALLOC_SIZE     PAGE_ALIGN(16384)
-#define MLX4_EN_ALLOC_ORDER    get_order(MLX4_EN_ALLOC_SIZE)
+
+#define MLX4_EN_ALLOC_PREFER_ORDER     PAGE_ALLOC_COSTLY_ORDER
 
 /* Receive fragment sizes; we use at most 3 fragments (for 9600 byte MTU
  * and 4K allocations) */
@@ -234,9 +235,10 @@ struct mlx4_en_tx_desc {
 #define MLX4_EN_CX3_HIGH_ID    0x1005
 
 struct mlx4_en_rx_alloc {
-       struct page *page;
-       dma_addr_t dma;
-       u16 offset;
+       struct page     *page;
+       dma_addr_t      dma;
+       u32             offset;
+       u32             size;
 };
 
 struct mlx4_en_tx_ring {
@@ -439,8 +441,6 @@ struct mlx4_en_frag_info {
        u16 frag_prefix_size;
        u16 frag_stride;
        u16 frag_align;
-       u16 last_offset;
-
 };
 
 #ifdef CONFIG_MLX4_EN_DCB
index 7be9788..967bae8 100644 (file)
@@ -3299,7 +3299,7 @@ static int myri10ge_resume(struct pci_dev *pdev)
        if (mgp == NULL)
                return -EINVAL;
        netdev = mgp->dev;
-       pci_set_power_state(pdev, 0);   /* zeros conf space as a side effect */
+       pci_set_power_state(pdev, PCI_D0);      /* zeros conf space as a side effect */
        msleep(5);              /* give card time to respond */
        pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
        if (vendor == 0xffff) {
index 7fb7e17..7779036 100644 (file)
@@ -633,6 +633,8 @@ struct pch_gbe_adapter {
        struct pci_dev *ptp_pdev;
 };
 
+#define pch_gbe_hw_to_adapter(hw)      container_of(hw, struct pch_gbe_adapter, hw)
+
 extern const char pch_driver_version[];
 
 /* pch_gbe_main.c */
index 5ae03e8..ff3ad70 100644 (file)
@@ -19,6 +19,7 @@
  */
 #include "pch_gbe.h"
 #include "pch_gbe_phy.h"
+#include "pch_gbe_api.h"
 
 /* bus type values */
 #define pch_gbe_bus_type_unknown       0
@@ -70,7 +71,9 @@ static s32 pch_gbe_plat_init_hw(struct pch_gbe_hw *hw)
 
        ret_val = pch_gbe_phy_get_id(hw);
        if (ret_val) {
-               pr_err("pch_gbe_phy_get_id error\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "pch_gbe_phy_get_id error\n");
                return ret_val;
        }
        pch_gbe_phy_init_setting(hw);
@@ -112,10 +115,12 @@ static void pch_gbe_plat_init_function_pointers(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     ENOSYS: Function is not registered
  */
-inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw)
+s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw)
 {
        if (!hw->reg) {
-               pr_err("ERROR: Registers not mapped\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: Registers not mapped\n");
                return -ENOSYS;
        }
        pch_gbe_plat_init_function_pointers(hw);
@@ -126,12 +131,15 @@ inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw)
  * pch_gbe_hal_get_bus_info - Obtain bus information for adapter
  * @hw:        Pointer to the HW structure
  */
-inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw)
+void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw)
 {
-       if (!hw->func->get_bus_info)
-               pr_err("ERROR: configuration\n");
-       else
-               hw->func->get_bus_info(hw);
+       if (!hw->func->get_bus_info) {
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
+               return;
+       }
+       hw->func->get_bus_info(hw);
 }
 
 /**
@@ -141,10 +149,12 @@ inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     ENOSYS: Function is not registered
  */
-inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw)
+s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw)
 {
        if (!hw->func->init_hw) {
-               pr_err("ERROR: configuration\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
                return -ENOSYS;
        }
        return hw->func->init_hw(hw);
@@ -159,7 +169,7 @@ inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     Negative value: Failed
  */
-inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset,
+s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset,
                                        u16 *data)
 {
        if (!hw->func->read_phy_reg)
@@ -176,7 +186,7 @@ inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset,
  *     0:      Successfully
  *     Negative value: Failed
  */
-inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset,
+s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset,
                                        u16 data)
 {
        if (!hw->func->write_phy_reg)
@@ -188,24 +198,30 @@ inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset,
  * pch_gbe_hal_phy_hw_reset - Hard PHY reset
  * @hw:            Pointer to the HW structure
  */
-inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw)
+void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw)
 {
-       if (!hw->func->reset_phy)
-               pr_err("ERROR: configuration\n");
-       else
-               hw->func->reset_phy(hw);
+       if (!hw->func->reset_phy) {
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
+               return;
+       }
+       hw->func->reset_phy(hw);
 }
 
 /**
  * pch_gbe_hal_phy_sw_reset - Soft PHY reset
  * @hw:            Pointer to the HW structure
  */
-inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw)
+void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw)
 {
-       if (!hw->func->sw_reset_phy)
-               pr_err("ERROR: configuration\n");
-       else
-               hw->func->sw_reset_phy(hw);
+       if (!hw->func->sw_reset_phy) {
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
+               return;
+       }
+       hw->func->sw_reset_phy(hw);
 }
 
 /**
@@ -215,10 +231,12 @@ inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw)
  *     0:      Successfully
  *     ENOSYS: Function is not registered
  */
-inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw)
+s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw)
 {
        if (!hw->func->read_mac_addr) {
-               pr_err("ERROR: configuration\n");
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "ERROR: configuration\n");
                return -ENOSYS;
        }
        return hw->func->read_mac_addr(hw);
@@ -228,7 +246,7 @@ inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw)
  * pch_gbe_hal_power_up_phy - Power up PHY
  * @hw:        Pointer to the HW structure
  */
-inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw)
+void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw)
 {
        if (hw->func->power_up_phy)
                hw->func->power_up_phy(hw);
@@ -238,7 +256,7 @@ inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw)
  * pch_gbe_hal_power_down_phy - Power down PHY
  * @hw:        Pointer to the HW structure
  */
-inline void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw)
+void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw)
 {
        if (hw->func->power_down_phy)
                hw->func->power_down_phy(hw);
index 24b787b..1129db0 100644 (file)
@@ -122,7 +122,7 @@ static int pch_gbe_set_settings(struct net_device *netdev,
        }
        ret = mii_ethtool_sset(&adapter->mii, ecmd);
        if (ret) {
-               pr_err("Error: mii_ethtool_sset\n");
+               netdev_err(netdev, "Error: mii_ethtool_sset\n");
                return ret;
        }
        hw->mac.link_speed = speed;
index 0c1c65a..ab1039a 100644 (file)
@@ -287,7 +287,7 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
 
-inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
+static inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
 {
        iowrite32(0x01, &hw->reg->MAC_ADDR_LOAD);
 }
@@ -300,6 +300,7 @@ inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
  */
 s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32  adr1a, adr1b;
 
        adr1a = ioread32(&hw->reg->mac_adr[0].high);
@@ -312,7 +313,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw)
        hw->mac.addr[4] = (u8)(adr1b & 0xFF);
        hw->mac.addr[5] = (u8)((adr1b >> 8) & 0xFF);
 
-       pr_debug("hw->mac.addr : %pM\n", hw->mac.addr);
+       netdev_dbg(adapter->netdev, "hw->mac.addr : %pM\n", hw->mac.addr);
        return 0;
 }
 
@@ -324,6 +325,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw)
 static void pch_gbe_wait_clr_bit(void *reg, u32 bit)
 {
        u32 tmp;
+
        /* wait busy */
        tmp = 1000;
        while ((ioread32(reg) & bit) && --tmp)
@@ -340,9 +342,10 @@ static void pch_gbe_wait_clr_bit(void *reg, u32 bit)
  */
 static void pch_gbe_mac_mar_set(struct pch_gbe_hw *hw, u8 * addr, u32 index)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32 mar_low, mar_high, adrmask;
 
-       pr_debug("index : 0x%x\n", index);
+       netdev_dbg(adapter->netdev, "index : 0x%x\n", index);
 
        /*
         * HW expects these in little endian so we reverse the byte order
@@ -468,10 +471,11 @@ static void pch_gbe_mac_mc_addr_list_update(struct pch_gbe_hw *hw,
  */
 s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        struct pch_gbe_mac_info *mac = &hw->mac;
        u32 rx_fctrl;
 
-       pr_debug("mac->fc = %u\n", mac->fc);
+       netdev_dbg(adapter->netdev, "mac->fc = %u\n", mac->fc);
 
        rx_fctrl = ioread32(&hw->reg->RX_FCTRL);
 
@@ -493,14 +497,16 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw)
                mac->tx_fc_enable = true;
                break;
        default:
-               pr_err("Flow control param set incorrectly\n");
+               netdev_err(adapter->netdev,
+                          "Flow control param set incorrectly\n");
                return -EINVAL;
        }
        if (mac->link_duplex == DUPLEX_HALF)
                rx_fctrl &= ~PCH_GBE_FL_CTRL_EN;
        iowrite32(rx_fctrl, &hw->reg->RX_FCTRL);
-       pr_debug("RX_FCTRL reg : 0x%08x  mac->tx_fc_enable : %d\n",
-                ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable);
+       netdev_dbg(adapter->netdev,
+                  "RX_FCTRL reg : 0x%08x  mac->tx_fc_enable : %d\n",
+                  ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable);
        return 0;
 }
 
@@ -511,10 +517,11 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw)
  */
 static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32 addr_mask;
 
-       pr_debug("wu_evt : 0x%08x  ADDR_MASK reg : 0x%08x\n",
-                wu_evt, ioread32(&hw->reg->ADDR_MASK));
+       netdev_dbg(adapter->netdev, "wu_evt : 0x%08x  ADDR_MASK reg : 0x%08x\n",
+                  wu_evt, ioread32(&hw->reg->ADDR_MASK));
 
        if (wu_evt) {
                /* Set Wake-On-Lan address mask */
@@ -546,6 +553,7 @@ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt)
 u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
                        u16 data)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        u32 data_out = 0;
        unsigned int i;
        unsigned long flags;
@@ -558,7 +566,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
                udelay(20);
        }
        if (i == 0) {
-               pr_err("pch-gbe.miim won't go Ready\n");
+               netdev_err(adapter->netdev, "pch-gbe.miim won't go Ready\n");
                spin_unlock_irqrestore(&hw->miim_lock, flags);
                return 0;       /* No way to indicate timeout error */
        }
@@ -573,9 +581,9 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
        }
        spin_unlock_irqrestore(&hw->miim_lock, flags);
 
-       pr_debug("PHY %s: reg=%d, data=0x%04X\n",
-                dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg,
-                dir == PCH_GBE_MIIM_OPER_READ ? data_out : data);
+       netdev_dbg(adapter->netdev, "PHY %s: reg=%d, data=0x%04X\n",
+                  dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg,
+                  dir == PCH_GBE_MIIM_OPER_READ ? data_out : data);
        return (u16) data_out;
 }
 
@@ -585,6 +593,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg,
  */
 static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        unsigned long tmp2, tmp3;
 
        /* Set Pause packet */
@@ -606,10 +615,13 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw)
        /* Transmit Pause Packet */
        iowrite32(PCH_GBE_PS_PKT_RQ, &hw->reg->PAUSE_REQ);
 
-       pr_debug("PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
-                ioread32(&hw->reg->PAUSE_PKT1), ioread32(&hw->reg->PAUSE_PKT2),
-                ioread32(&hw->reg->PAUSE_PKT3), ioread32(&hw->reg->PAUSE_PKT4),
-                ioread32(&hw->reg->PAUSE_PKT5));
+       netdev_dbg(adapter->netdev,
+                  "PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                  ioread32(&hw->reg->PAUSE_PKT1),
+                  ioread32(&hw->reg->PAUSE_PKT2),
+                  ioread32(&hw->reg->PAUSE_PKT3),
+                  ioread32(&hw->reg->PAUSE_PKT4),
+                  ioread32(&hw->reg->PAUSE_PKT5));
 
        return;
 }
@@ -624,15 +636,15 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw)
  */
 static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter)
 {
-       adapter->tx_ring = kzalloc(sizeof(*adapter->tx_ring), GFP_KERNEL);
+       adapter->tx_ring = devm_kzalloc(&adapter->pdev->dev,
+                                       sizeof(*adapter->tx_ring), GFP_KERNEL);
        if (!adapter->tx_ring)
                return -ENOMEM;
 
-       adapter->rx_ring = kzalloc(sizeof(*adapter->rx_ring), GFP_KERNEL);
-       if (!adapter->rx_ring) {
-               kfree(adapter->tx_ring);
+       adapter->rx_ring = devm_kzalloc(&adapter->pdev->dev,
+                                       sizeof(*adapter->rx_ring), GFP_KERNEL);
+       if (!adapter->rx_ring)
                return -ENOMEM;
-       }
        return 0;
 }
 
@@ -669,7 +681,7 @@ static int pch_gbe_init_phy(struct pch_gbe_adapter *adapter)
                        break;
        }
        adapter->hw.phy.addr = adapter->mii.phy_id;
-       pr_debug("phy_addr = %d\n", adapter->mii.phy_id);
+       netdev_dbg(netdev, "phy_addr = %d\n", adapter->mii.phy_id);
        if (addr == 32)
                return -EAGAIN;
        /* Selected the phy and isolate the rest */
@@ -758,13 +770,15 @@ void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter)
  */
 void pch_gbe_reset(struct pch_gbe_adapter *adapter)
 {
+       struct net_device *netdev = adapter->netdev;
+
        pch_gbe_mac_reset_hw(&adapter->hw);
        /* reprogram multicast address register after reset */
-       pch_gbe_set_multi(adapter->netdev);
+       pch_gbe_set_multi(netdev);
        /* Setup the receive address. */
        pch_gbe_mac_init_rx_addrs(&adapter->hw, PCH_GBE_MAR_ENTRIES);
        if (pch_gbe_hal_init_hw(&adapter->hw))
-               pr_err("Hardware Error\n");
+               netdev_err(netdev, "Hardware Error\n");
 }
 
 /**
@@ -778,7 +792,7 @@ static void pch_gbe_free_irq(struct pch_gbe_adapter *adapter)
        free_irq(adapter->pdev->irq, netdev);
        if (adapter->have_msi) {
                pci_disable_msi(adapter->pdev);
-               pr_debug("call pci_disable_msi\n");
+               netdev_dbg(netdev, "call pci_disable_msi\n");
        }
 }
 
@@ -795,7 +809,8 @@ static void pch_gbe_irq_disable(struct pch_gbe_adapter *adapter)
        ioread32(&hw->reg->INT_ST);
        synchronize_irq(adapter->pdev->irq);
 
-       pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN));
+       netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n",
+                  ioread32(&hw->reg->INT_EN));
 }
 
 /**
@@ -809,7 +824,8 @@ static void pch_gbe_irq_enable(struct pch_gbe_adapter *adapter)
        if (likely(atomic_dec_and_test(&adapter->irq_sem)))
                iowrite32(PCH_GBE_INT_ENABLE_MASK, &hw->reg->INT_EN);
        ioread32(&hw->reg->INT_ST);
-       pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN));
+       netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n",
+                  ioread32(&hw->reg->INT_EN));
 }
 
 
@@ -846,9 +862,9 @@ static void pch_gbe_configure_tx(struct pch_gbe_adapter *adapter)
        struct pch_gbe_hw *hw = &adapter->hw;
        u32 tdba, tdlen, dctrl;
 
-       pr_debug("dma addr = 0x%08llx  size = 0x%08x\n",
-                (unsigned long long)adapter->tx_ring->dma,
-                adapter->tx_ring->size);
+       netdev_dbg(adapter->netdev, "dma addr = 0x%08llx  size = 0x%08x\n",
+                  (unsigned long long)adapter->tx_ring->dma,
+                  adapter->tx_ring->size);
 
        /* Setup the HW Tx Head and Tail descriptor pointers */
        tdba = adapter->tx_ring->dma;
@@ -894,9 +910,9 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter)
        struct pch_gbe_hw *hw = &adapter->hw;
        u32 rdba, rdlen, rxdma;
 
-       pr_debug("dma adr = 0x%08llx  size = 0x%08x\n",
-                (unsigned long long)adapter->rx_ring->dma,
-                adapter->rx_ring->size);
+       netdev_dbg(adapter->netdev, "dma adr = 0x%08llx  size = 0x%08x\n",
+                  (unsigned long long)adapter->rx_ring->dma,
+                  adapter->rx_ring->size);
 
        pch_gbe_mac_force_mac_fc(hw);
 
@@ -907,9 +923,10 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter)
        rxdma &= ~PCH_GBE_RX_DMA_EN;
        iowrite32(rxdma, &hw->reg->DMA_CTRL);
 
-       pr_debug("MAC_RX_EN reg = 0x%08x  DMA_CTRL reg = 0x%08x\n",
-                ioread32(&hw->reg->MAC_RX_EN),
-                ioread32(&hw->reg->DMA_CTRL));
+       netdev_dbg(adapter->netdev,
+                  "MAC_RX_EN reg = 0x%08x  DMA_CTRL reg = 0x%08x\n",
+                  ioread32(&hw->reg->MAC_RX_EN),
+                  ioread32(&hw->reg->DMA_CTRL));
 
        /* Setup the HW Rx Head and Tail Descriptor Pointers and
         * the Base and Length of the Rx Descriptor Ring */
@@ -977,7 +994,8 @@ static void pch_gbe_clean_tx_ring(struct pch_gbe_adapter *adapter,
                buffer_info = &tx_ring->buffer_info[i];
                pch_gbe_unmap_and_free_tx_resource(adapter, buffer_info);
        }
-       pr_debug("call pch_gbe_unmap_and_free_tx_resource() %d count\n", i);
+       netdev_dbg(adapter->netdev,
+                  "call pch_gbe_unmap_and_free_tx_resource() %d count\n", i);
 
        size = (unsigned long)sizeof(struct pch_gbe_buffer) * tx_ring->count;
        memset(tx_ring->buffer_info, 0, size);
@@ -1009,7 +1027,8 @@ pch_gbe_clean_rx_ring(struct pch_gbe_adapter *adapter,
                buffer_info = &rx_ring->buffer_info[i];
                pch_gbe_unmap_and_free_rx_resource(adapter, buffer_info);
        }
-       pr_debug("call pch_gbe_unmap_and_free_rx_resource() %d count\n", i);
+       netdev_dbg(adapter->netdev,
+                  "call pch_gbe_unmap_and_free_rx_resource() %d count\n", i);
        size = (unsigned long)sizeof(struct pch_gbe_buffer) * rx_ring->count;
        memset(rx_ring->buffer_info, 0, size);
 
@@ -1087,7 +1106,7 @@ static void pch_gbe_watchdog(unsigned long data)
        struct net_device *netdev = adapter->netdev;
        struct pch_gbe_hw *hw = &adapter->hw;
 
-       pr_debug("right now = %ld\n", jiffies);
+       netdev_dbg(netdev, "right now = %ld\n", jiffies);
 
        pch_gbe_update_stats(adapter);
        if ((mii_link_ok(&adapter->mii)) && (!netif_carrier_ok(netdev))) {
@@ -1095,7 +1114,7 @@ static void pch_gbe_watchdog(unsigned long data)
                netdev->tx_queue_len = adapter->tx_queue_len;
                /* mii library handles link maintenance tasks */
                if (mii_ethtool_gset(&adapter->mii, &cmd)) {
-                       pr_err("ethtool get setting Error\n");
+                       netdev_err(netdev, "ethtool get setting Error\n");
                        mod_timer(&adapter->watchdog_timer,
                                  round_jiffies(jiffies +
                                                PCH_GBE_WATCHDOG_PERIOD));
@@ -1213,7 +1232,7 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter,
                                          buffer_info->length,
                                          DMA_TO_DEVICE);
        if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
-               pr_err("TX DMA map failed\n");
+               netdev_err(adapter->netdev, "TX DMA map failed\n");
                buffer_info->dma = 0;
                buffer_info->time_stamp = 0;
                tx_ring->next_to_use = ring_num;
@@ -1333,13 +1352,13 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
        /* When request status is no interruption factor */
        if (unlikely(!int_st))
                return IRQ_NONE;        /* Not our interrupt. End processing. */
-       pr_debug("%s occur int_st = 0x%08x\n", __func__, int_st);
+       netdev_dbg(netdev, "%s occur int_st = 0x%08x\n", __func__, int_st);
        if (int_st & PCH_GBE_INT_RX_FRAME_ERR)
                adapter->stats.intr_rx_frame_err_count++;
        if (int_st & PCH_GBE_INT_RX_FIFO_ERR)
                if (!adapter->rx_stop_flag) {
                        adapter->stats.intr_rx_fifo_err_count++;
-                       pr_debug("Rx fifo over run\n");
+                       netdev_dbg(netdev, "Rx fifo over run\n");
                        adapter->rx_stop_flag = true;
                        int_en = ioread32(&hw->reg->INT_EN);
                        iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR),
@@ -1359,7 +1378,7 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
        /* When Rx descriptor is empty  */
        if ((int_st & PCH_GBE_INT_RX_DSC_EMP)) {
                adapter->stats.intr_rx_dsc_empty_count++;
-               pr_debug("Rx descriptor is empty\n");
+               netdev_dbg(netdev, "Rx descriptor is empty\n");
                int_en = ioread32(&hw->reg->INT_EN);
                iowrite32((int_en & ~PCH_GBE_INT_RX_DSC_EMP), &hw->reg->INT_EN);
                if (hw->mac.tx_fc_enable) {
@@ -1382,8 +1401,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data)
                        __napi_schedule(&adapter->napi);
                }
        }
-       pr_debug("return = 0x%08x  INT_EN reg = 0x%08x\n",
-                IRQ_HANDLED, ioread32(&hw->reg->INT_EN));
+       netdev_dbg(netdev, "return = 0x%08x  INT_EN reg = 0x%08x\n",
+                  IRQ_HANDLED, ioread32(&hw->reg->INT_EN));
        return IRQ_HANDLED;
 }
 
@@ -1437,9 +1456,10 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter,
                rx_desc->buffer_addr = (buffer_info->dma);
                rx_desc->gbec_status = DSC_INIT16;
 
-               pr_debug("i = %d  buffer_info->dma = 0x08%llx  buffer_info->length = 0x%x\n",
-                        i, (unsigned long long)buffer_info->dma,
-                        buffer_info->length);
+               netdev_dbg(netdev,
+                          "i = %d  buffer_info->dma = 0x08%llx  buffer_info->length = 0x%x\n",
+                          i, (unsigned long long)buffer_info->dma,
+                          buffer_info->length);
 
                if (unlikely(++i == rx_ring->count))
                        i = 0;
@@ -1531,12 +1551,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
        bool cleaned = false;
        int unused, thresh;
 
-       pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean);
+       netdev_dbg(adapter->netdev, "next_to_clean : %d\n",
+                  tx_ring->next_to_clean);
 
        i = tx_ring->next_to_clean;
        tx_desc = PCH_GBE_TX_DESC(*tx_ring, i);
-       pr_debug("gbec_status:0x%04x  dma_status:0x%04x\n",
-                tx_desc->gbec_status, tx_desc->dma_status);
+       netdev_dbg(adapter->netdev, "gbec_status:0x%04x  dma_status:0x%04x\n",
+                  tx_desc->gbec_status, tx_desc->dma_status);
 
        unused = PCH_GBE_DESC_UNUSED(tx_ring);
        thresh = tx_ring->count - PCH_GBE_TX_WEIGHT;
@@ -1544,8 +1565,10 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
        {  /* current marked clean, tx queue filling up, do extra clean */
                int j, k;
                if (unused < 8) {  /* tx queue nearly full */
-                       pr_debug("clean_tx: transmit queue warning (%x,%x) unused=%d\n",
-                               tx_ring->next_to_clean,tx_ring->next_to_use,unused);
+                       netdev_dbg(adapter->netdev,
+                                  "clean_tx: transmit queue warning (%x,%x) unused=%d\n",
+                                  tx_ring->next_to_clean, tx_ring->next_to_use,
+                                  unused);
                }
 
                /* current marked clean, scan for more that need cleaning. */
@@ -1557,49 +1580,56 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
                        if (++k >= tx_ring->count) k = 0;  /*increment, wrap*/
                }
                if (j < PCH_GBE_TX_WEIGHT) {
-                       pr_debug("clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n",
-                               unused,j, i,k, tx_ring->next_to_use, tx_desc->gbec_status);
+                       netdev_dbg(adapter->netdev,
+                                  "clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n",
+                                  unused, j, i, k, tx_ring->next_to_use,
+                                  tx_desc->gbec_status);
                        i = k;  /*found one to clean, usu gbec_status==2000.*/
                }
        }
 
        while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) {
-               pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status);
+               netdev_dbg(adapter->netdev, "gbec_status:0x%04x\n",
+                          tx_desc->gbec_status);
                buffer_info = &tx_ring->buffer_info[i];
                skb = buffer_info->skb;
                cleaned = true;
 
                if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_ABT)) {
                        adapter->stats.tx_aborted_errors++;
-                       pr_err("Transfer Abort Error\n");
+                       netdev_err(adapter->netdev, "Transfer Abort Error\n");
                } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CRSER)
                          ) {
                        adapter->stats.tx_carrier_errors++;
-                       pr_err("Transfer Carrier Sense Error\n");
+                       netdev_err(adapter->netdev,
+                                  "Transfer Carrier Sense Error\n");
                } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_EXCOL)
                          ) {
                        adapter->stats.tx_aborted_errors++;
-                       pr_err("Transfer Collision Abort Error\n");
+                       netdev_err(adapter->netdev,
+                                  "Transfer Collision Abort Error\n");
                } else if ((tx_desc->gbec_status &
                            (PCH_GBE_TXD_GMAC_STAT_SNGCOL |
                             PCH_GBE_TXD_GMAC_STAT_MLTCOL))) {
                        adapter->stats.collisions++;
                        adapter->stats.tx_packets++;
                        adapter->stats.tx_bytes += skb->len;
-                       pr_debug("Transfer Collision\n");
+                       netdev_dbg(adapter->netdev, "Transfer Collision\n");
                } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CMPLT)
                          ) {
                        adapter->stats.tx_packets++;
                        adapter->stats.tx_bytes += skb->len;
                }
                if (buffer_info->mapped) {
-                       pr_debug("unmap buffer_info->dma : %d\n", i);
+                       netdev_dbg(adapter->netdev,
+                                  "unmap buffer_info->dma : %d\n", i);
                        dma_unmap_single(&adapter->pdev->dev, buffer_info->dma,
                                         buffer_info->length, DMA_TO_DEVICE);
                        buffer_info->mapped = false;
                }
                if (buffer_info->skb) {
-                       pr_debug("trim buffer_info->skb : %d\n", i);
+                       netdev_dbg(adapter->netdev,
+                                  "trim buffer_info->skb : %d\n", i);
                        skb_trim(buffer_info->skb, 0);
                }
                tx_desc->gbec_status = DSC_INIT16;
@@ -1613,8 +1643,9 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
                        break;
                }
        }
-       pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n",
-                cleaned_count);
+       netdev_dbg(adapter->netdev,
+                  "called pch_gbe_unmap_and_free_tx_resource() %d count\n",
+                  cleaned_count);
        if (cleaned_count > 0)  { /*skip this if nothing cleaned*/
                /* Recover from running out of Tx resources in xmit_frame */
                spin_lock(&tx_ring->tx_lock);
@@ -1622,12 +1653,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter,
                {
                        netif_wake_queue(adapter->netdev);
                        adapter->stats.tx_restart_count++;
-                       pr_debug("Tx wake queue\n");
+                       netdev_dbg(adapter->netdev, "Tx wake queue\n");
                }
 
                tx_ring->next_to_clean = i;
 
-               pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean);
+               netdev_dbg(adapter->netdev, "next_to_clean : %d\n",
+                          tx_ring->next_to_clean);
                spin_unlock(&tx_ring->tx_lock);
        }
        return cleaned;
@@ -1684,22 +1716,22 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
                                   buffer_info->length, DMA_FROM_DEVICE);
                buffer_info->mapped = false;
 
-               pr_debug("RxDecNo = 0x%04x  Status[DMA:0x%02x GBE:0x%04x "
-                        "TCP:0x%08x]  BufInf = 0x%p\n",
-                        i, dma_status, gbec_status, tcp_ip_status,
-                        buffer_info);
+               netdev_dbg(netdev,
+                          "RxDecNo = 0x%04x  Status[DMA:0x%02x GBE:0x%04x TCP:0x%08x]  BufInf = 0x%p\n",
+                          i, dma_status, gbec_status, tcp_ip_status,
+                          buffer_info);
                /* Error check */
                if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NOTOCTAL)) {
                        adapter->stats.rx_frame_errors++;
-                       pr_err("Receive Not Octal Error\n");
+                       netdev_err(netdev, "Receive Not Octal Error\n");
                } else if (unlikely(gbec_status &
                                PCH_GBE_RXD_GMAC_STAT_NBLERR)) {
                        adapter->stats.rx_frame_errors++;
-                       pr_err("Receive Nibble Error\n");
+                       netdev_err(netdev, "Receive Nibble Error\n");
                } else if (unlikely(gbec_status &
                                PCH_GBE_RXD_GMAC_STAT_CRCERR)) {
                        adapter->stats.rx_crc_errors++;
-                       pr_err("Receive CRC Error\n");
+                       netdev_err(netdev, "Receive CRC Error\n");
                } else {
                        /* get receive length */
                        /* length convert[-3], length includes FCS length */
@@ -1730,8 +1762,9 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter,
 
                        napi_gro_receive(&adapter->napi, skb);
                        (*work_done)++;
-                       pr_debug("Receive skb->ip_summed: %d length: %d\n",
-                                skb->ip_summed, length);
+                       netdev_dbg(netdev,
+                                  "Receive skb->ip_summed: %d length: %d\n",
+                                  skb->ip_summed, length);
                }
                /* return some buffers to hardware, one at a time is too slow */
                if (unlikely(cleaned_count >= PCH_GBE_RX_BUFFER_WRITE)) {
@@ -1787,10 +1820,10 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter,
                tx_desc = PCH_GBE_TX_DESC(*tx_ring, desNo);
                tx_desc->gbec_status = DSC_INIT16;
        }
-       pr_debug("tx_ring->desc = 0x%p  tx_ring->dma = 0x%08llx\n"
-                "next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
-                tx_ring->desc, (unsigned long long)tx_ring->dma,
-                tx_ring->next_to_clean, tx_ring->next_to_use);
+       netdev_dbg(adapter->netdev,
+                  "tx_ring->desc = 0x%p  tx_ring->dma = 0x%08llx next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
+                  tx_ring->desc, (unsigned long long)tx_ring->dma,
+                  tx_ring->next_to_clean, tx_ring->next_to_use);
        return 0;
 }
 
@@ -1829,10 +1862,10 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter,
                rx_desc = PCH_GBE_RX_DESC(*rx_ring, desNo);
                rx_desc->gbec_status = DSC_INIT16;
        }
-       pr_debug("rx_ring->desc = 0x%p  rx_ring->dma = 0x%08llx "
-                "next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
-                rx_ring->desc, (unsigned long long)rx_ring->dma,
-                rx_ring->next_to_clean, rx_ring->next_to_use);
+       netdev_dbg(adapter->netdev,
+                  "rx_ring->desc = 0x%p  rx_ring->dma = 0x%08llx next_to_clean = 0x%08x  next_to_use = 0x%08x\n",
+                  rx_ring->desc, (unsigned long long)rx_ring->dma,
+                  rx_ring->next_to_clean, rx_ring->next_to_use);
        return 0;
 }
 
@@ -1886,9 +1919,9 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter)
        flags = IRQF_SHARED;
        adapter->have_msi = false;
        err = pci_enable_msi(adapter->pdev);
-       pr_debug("call pci_enable_msi\n");
+       netdev_dbg(netdev, "call pci_enable_msi\n");
        if (err) {
-               pr_debug("call pci_enable_msi - Error: %d\n", err);
+               netdev_dbg(netdev, "call pci_enable_msi - Error: %d\n", err);
        } else {
                flags = 0;
                adapter->have_msi = true;
@@ -1896,9 +1929,11 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter)
        err = request_irq(adapter->pdev->irq, &pch_gbe_intr,
                          flags, netdev->name, netdev);
        if (err)
-               pr_err("Unable to allocate interrupt Error: %d\n", err);
-       pr_debug("adapter->have_msi : %d  flags : 0x%04x  return : 0x%04x\n",
-                adapter->have_msi, flags, err);
+               netdev_err(netdev, "Unable to allocate interrupt Error: %d\n",
+                          err);
+       netdev_dbg(netdev,
+                  "adapter->have_msi : %d  flags : 0x%04x  return : 0x%04x\n",
+                  adapter->have_msi, flags, err);
        return err;
 }
 
@@ -1919,7 +1954,7 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter)
 
        /* Ensure we have a valid MAC */
        if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
-               pr_err("Error: Invalid MAC address\n");
+               netdev_err(netdev, "Error: Invalid MAC address\n");
                goto out;
        }
 
@@ -1933,12 +1968,14 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter)
 
        err = pch_gbe_request_irq(adapter);
        if (err) {
-               pr_err("Error: can't bring device up - irq request failed\n");
+               netdev_err(netdev,
+                          "Error: can't bring device up - irq request failed\n");
                goto out;
        }
        err = pch_gbe_alloc_rx_buffers_pool(adapter, rx_ring, rx_ring->count);
        if (err) {
-               pr_err("Error: can't bring device up - alloc rx buffers pool failed\n");
+               netdev_err(netdev,
+                          "Error: can't bring device up - alloc rx buffers pool failed\n");
                goto freeirq;
        }
        pch_gbe_alloc_tx_buffers(adapter, tx_ring);
@@ -2015,11 +2052,11 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter)
 
        /* Initialize the hardware-specific values */
        if (pch_gbe_hal_setup_init_funcs(hw)) {
-               pr_err("Hardware Initialization Failure\n");
+               netdev_err(netdev, "Hardware Initialization Failure\n");
                return -EIO;
        }
        if (pch_gbe_alloc_queues(adapter)) {
-               pr_err("Unable to allocate memory for queues\n");
+               netdev_err(netdev, "Unable to allocate memory for queues\n");
                return -ENOMEM;
        }
        spin_lock_init(&adapter->hw.miim_lock);
@@ -2030,9 +2067,10 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter)
 
        pch_gbe_init_stats(adapter);
 
-       pr_debug("rx_buffer_len : %d  mac.min_frame_size : %d  mac.max_frame_size : %d\n",
-                (u32) adapter->rx_buffer_len,
-                hw->mac.min_frame_size, hw->mac.max_frame_size);
+       netdev_dbg(netdev,
+                  "rx_buffer_len : %d  mac.min_frame_size : %d  mac.max_frame_size : %d\n",
+                  (u32) adapter->rx_buffer_len,
+                  hw->mac.min_frame_size, hw->mac.max_frame_size);
        return 0;
 }
 
@@ -2061,7 +2099,7 @@ static int pch_gbe_open(struct net_device *netdev)
        err = pch_gbe_up(adapter);
        if (err)
                goto err_up;
-       pr_debug("Success End\n");
+       netdev_dbg(netdev, "Success End\n");
        return 0;
 
 err_up:
@@ -2072,7 +2110,7 @@ err_setup_rx:
        pch_gbe_free_tx_resources(adapter, adapter->tx_ring);
 err_setup_tx:
        pch_gbe_reset(adapter);
-       pr_err("Error End\n");
+       netdev_err(netdev, "Error End\n");
        return err;
 }
 
@@ -2116,8 +2154,9 @@ static int pch_gbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
        if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) {
                netif_stop_queue(netdev);
                spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
-               pr_debug("Return : BUSY  next_to use : 0x%08x  next_to clean : 0x%08x\n",
-                        tx_ring->next_to_use, tx_ring->next_to_clean);
+               netdev_dbg(netdev,
+                          "Return : BUSY  next_to use : 0x%08x  next_to clean : 0x%08x\n",
+                          tx_ring->next_to_use, tx_ring->next_to_clean);
                return NETDEV_TX_BUSY;
        }
 
@@ -2152,7 +2191,7 @@ static void pch_gbe_set_multi(struct net_device *netdev)
        int i;
        int mc_count;
 
-       pr_debug("netdev->flags : 0x%08x\n", netdev->flags);
+       netdev_dbg(netdev, "netdev->flags : 0x%08x\n", netdev->flags);
 
        /* Check for Promiscuous and All Multicast modes */
        rctl = ioread32(&hw->reg->RX_MODE);
@@ -2192,7 +2231,8 @@ static void pch_gbe_set_multi(struct net_device *netdev)
                                        PCH_GBE_MAR_ENTRIES);
        kfree(mta_list);
 
-       pr_debug("RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x  netdev->mc_count : 0x%08x\n",
+       netdev_dbg(netdev,
+                "RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x  netdev->mc_count : 0x%08x\n",
                 ioread32(&hw->reg->RX_MODE), mc_count);
 }
 
@@ -2218,12 +2258,12 @@ static int pch_gbe_set_mac(struct net_device *netdev, void *addr)
                pch_gbe_mac_mar_set(&adapter->hw, adapter->hw.mac.addr, 0);
                ret_val = 0;
        }
-       pr_debug("ret_val : 0x%08x\n", ret_val);
-       pr_debug("dev_addr : %pM\n", netdev->dev_addr);
-       pr_debug("mac_addr : %pM\n", adapter->hw.mac.addr);
-       pr_debug("MAC_ADR1AB reg : 0x%08x 0x%08x\n",
-                ioread32(&adapter->hw.reg->mac_adr[0].high),
-                ioread32(&adapter->hw.reg->mac_adr[0].low));
+       netdev_dbg(netdev, "ret_val : 0x%08x\n", ret_val);
+       netdev_dbg(netdev, "dev_addr : %pM\n", netdev->dev_addr);
+       netdev_dbg(netdev, "mac_addr : %pM\n", adapter->hw.mac.addr);
+       netdev_dbg(netdev, "MAC_ADR1AB reg : 0x%08x 0x%08x\n",
+                  ioread32(&adapter->hw.reg->mac_adr[0].high),
+                  ioread32(&adapter->hw.reg->mac_adr[0].low));
        return ret_val;
 }
 
@@ -2245,7 +2285,7 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
        max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
        if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
                (max_frame > PCH_GBE_MAX_JUMBO_FRAME_SIZE)) {
-               pr_err("Invalid MTU setting\n");
+               netdev_err(netdev, "Invalid MTU setting\n");
                return -EINVAL;
        }
        if (max_frame <= PCH_GBE_FRAME_SIZE_2048)
@@ -2274,9 +2314,10 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu)
                adapter->hw.mac.max_frame_size = max_frame;
        }
 
-       pr_debug("max_frame : %d  rx_buffer_len : %d  mtu : %d  max_frame_size : %d\n",
-                max_frame, (u32) adapter->rx_buffer_len, netdev->mtu,
-                adapter->hw.mac.max_frame_size);
+       netdev_dbg(netdev,
+                  "max_frame : %d  rx_buffer_len : %d  mtu : %d  max_frame_size : %d\n",
+                  max_frame, (u32) adapter->rx_buffer_len, netdev->mtu,
+                  adapter->hw.mac.max_frame_size);
        return 0;
 }
 
@@ -2317,7 +2358,7 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
 {
        struct pch_gbe_adapter *adapter = netdev_priv(netdev);
 
-       pr_debug("cmd : 0x%04x\n", cmd);
+       netdev_dbg(netdev, "cmd : 0x%04x\n", cmd);
 
        if (cmd == SIOCSHWTSTAMP)
                return hwtstamp_ioctl(netdev, ifr, cmd);
@@ -2354,7 +2395,7 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
        bool poll_end_flag = false;
        bool cleaned = false;
 
-       pr_debug("budget : %d\n", budget);
+       netdev_dbg(adapter->netdev, "budget : %d\n", budget);
 
        pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget);
        cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring);
@@ -2377,8 +2418,9 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget)
                pch_gbe_enable_dma_rx(&adapter->hw);
        }
 
-       pr_debug("poll_end_flag : %d  work_done : %d  budget : %d\n",
-                poll_end_flag, work_done, budget);
+       netdev_dbg(adapter->netdev,
+                  "poll_end_flag : %d  work_done : %d  budget : %d\n",
+                  poll_end_flag, work_done, budget);
 
        return work_done;
 }
@@ -2435,7 +2477,7 @@ static pci_ers_result_t pch_gbe_io_slot_reset(struct pci_dev *pdev)
        struct pch_gbe_hw *hw = &adapter->hw;
 
        if (pci_enable_device(pdev)) {
-               pr_err("Cannot re-enable PCI device after reset\n");
+               netdev_err(netdev, "Cannot re-enable PCI device after reset\n");
                return PCI_ERS_RESULT_DISCONNECT;
        }
        pci_set_master(pdev);
@@ -2455,7 +2497,8 @@ static void pch_gbe_io_resume(struct pci_dev *pdev)
 
        if (netif_running(netdev)) {
                if (pch_gbe_up(adapter)) {
-                       pr_debug("can't bring device back up after reset\n");
+                       netdev_dbg(netdev,
+                                  "can't bring device back up after reset\n");
                        return;
                }
        }
@@ -2509,7 +2552,7 @@ static int pch_gbe_resume(struct device *device)
 
        err = pci_enable_device(pdev);
        if (err) {
-               pr_err("Cannot enable PCI device from suspend\n");
+               netdev_err(netdev, "Cannot enable PCI device from suspend\n");
                return err;
        }
        pci_set_master(pdev);
@@ -2545,13 +2588,7 @@ static void pch_gbe_remove(struct pci_dev *pdev)
 
        pch_gbe_hal_phy_hw_reset(&adapter->hw);
 
-       kfree(adapter->tx_ring);
-       kfree(adapter->rx_ring);
-
-       iounmap(adapter->hw.reg);
-       pci_release_regions(pdev);
        free_netdev(netdev);
-       pci_disable_device(pdev);
 }
 
 static int pch_gbe_probe(struct pci_dev *pdev,
@@ -2561,7 +2598,7 @@ static int pch_gbe_probe(struct pci_dev *pdev,
        struct pch_gbe_adapter *adapter;
        int ret;
 
-       ret = pci_enable_device(pdev);
+       ret = pcim_enable_device(pdev);
        if (ret)
                return ret;
 
@@ -2574,24 +2611,22 @@ static int pch_gbe_probe(struct pci_dev *pdev,
                        if (ret) {
                                dev_err(&pdev->dev, "ERR: No usable DMA "
                                        "configuration, aborting\n");
-                               goto err_disable_device;
+                               return ret;
                        }
                }
        }
 
-       ret = pci_request_regions(pdev, KBUILD_MODNAME);
+       ret = pcim_iomap_regions(pdev, 1 << PCH_GBE_PCI_BAR, pci_name(pdev));
        if (ret) {
                dev_err(&pdev->dev,
                        "ERR: Can't reserve PCI I/O and memory resources\n");
-               goto err_disable_device;
+               return ret;
        }
        pci_set_master(pdev);
 
        netdev = alloc_etherdev((int)sizeof(struct pch_gbe_adapter));
-       if (!netdev) {
-               ret = -ENOMEM;
-               goto err_release_pci;
-       }
+       if (!netdev)
+               return -ENOMEM;
        SET_NETDEV_DEV(netdev, &pdev->dev);
 
        pci_set_drvdata(pdev, netdev);
@@ -2599,18 +2634,14 @@ static int pch_gbe_probe(struct pci_dev *pdev,
        adapter->netdev = netdev;
        adapter->pdev = pdev;
        adapter->hw.back = adapter;
-       adapter->hw.reg = pci_iomap(pdev, PCH_GBE_PCI_BAR, 0);
-       if (!adapter->hw.reg) {
-               ret = -EIO;
-               dev_err(&pdev->dev, "Can't ioremap\n");
-               goto err_free_netdev;
-       }
+       adapter->hw.reg = pcim_iomap_table(pdev)[PCH_GBE_PCI_BAR];
 
        adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number,
                                               PCI_DEVFN(12, 4));
        if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
-               pr_err("Bad ptp filter\n");
-               return -EINVAL;
+               dev_err(&pdev->dev, "Bad ptp filter\n");
+               ret = -EINVAL;
+               goto err_free_netdev;
        }
 
        netdev->netdev_ops = &pch_gbe_netdev_ops;
@@ -2628,7 +2659,7 @@ static int pch_gbe_probe(struct pci_dev *pdev,
        /* setup the private structure */
        ret = pch_gbe_sw_init(adapter);
        if (ret)
-               goto err_iounmap;
+               goto err_free_netdev;
 
        /* Initialize PHY */
        ret = pch_gbe_init_phy(adapter);
@@ -2684,16 +2715,8 @@ static int pch_gbe_probe(struct pci_dev *pdev,
 
 err_free_adapter:
        pch_gbe_hal_phy_hw_reset(&adapter->hw);
-       kfree(adapter->tx_ring);
-       kfree(adapter->rx_ring);
-err_iounmap:
-       iounmap(adapter->hw.reg);
 err_free_netdev:
        free_netdev(netdev);
-err_release_pci:
-       pci_release_regions(pdev);
-err_disable_device:
-       pci_disable_device(pdev);
        return ret;
 }
 
index 8653c3b..cf7c9b3 100644 (file)
@@ -237,16 +237,17 @@ static int pch_gbe_validate_option(int *value,
        case enable_option:
                switch (*value) {
                case OPTION_ENABLED:
-                       pr_debug("%s Enabled\n", opt->name);
+                       netdev_dbg(adapter->netdev, "%s Enabled\n", opt->name);
                        return 0;
                case OPTION_DISABLED:
-                       pr_debug("%s Disabled\n", opt->name);
+                       netdev_dbg(adapter->netdev, "%s Disabled\n", opt->name);
                        return 0;
                }
                break;
        case range_option:
                if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
-                       pr_debug("%s set to %i\n", opt->name, *value);
+                       netdev_dbg(adapter->netdev, "%s set to %i\n",
+                                  opt->name, *value);
                        return 0;
                }
                break;
@@ -258,7 +259,8 @@ static int pch_gbe_validate_option(int *value,
                        ent = &opt->arg.l.p[i];
                        if (*value == ent->i) {
                                if (ent->str[0] != '\0')
-                                       pr_debug("%s\n", ent->str);
+                                       netdev_dbg(adapter->netdev, "%s\n",
+                                                  ent->str);
                                return 0;
                        }
                }
@@ -268,8 +270,8 @@ static int pch_gbe_validate_option(int *value,
                BUG();
        }
 
-       pr_debug("Invalid %s value specified (%i) %s\n",
-                opt->name, *value, opt->err);
+       netdev_dbg(adapter->netdev, "Invalid %s value specified (%i) %s\n",
+                  opt->name, *value, opt->err);
        *value = opt->def;
        return -1;
 }
@@ -318,7 +320,8 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                                         .p = an_list} }
                };
                if (speed || dplx) {
-                       pr_debug("AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n");
+                       netdev_dbg(adapter->netdev,
+                                  "AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n");
                        hw->phy.autoneg_advertised = opt.def;
                } else {
                        int tmp = AutoNeg;
@@ -332,13 +335,16 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
        case 0:
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                if ((speed || dplx))
-                       pr_debug("Speed and duplex autonegotiation enabled\n");
+                       netdev_dbg(adapter->netdev,
+                                  "Speed and duplex autonegotiation enabled\n");
                hw->mac.link_speed = SPEED_10;
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case HALF_DUPLEX:
-               pr_debug("Half Duplex specified without Speed\n");
-               pr_debug("Using Autonegotiation at Half Duplex only\n");
+               netdev_dbg(adapter->netdev,
+                          "Half Duplex specified without Speed\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at Half Duplex only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF |
                                                PHY_ADVERTISE_100_HALF;
@@ -346,8 +352,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case FULL_DUPLEX:
-               pr_debug("Full Duplex specified without Speed\n");
-               pr_debug("Using Autonegotiation at Full Duplex only\n");
+               netdev_dbg(adapter->netdev,
+                          "Full Duplex specified without Speed\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at Full Duplex only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_10_FULL |
                                                PHY_ADVERTISE_100_FULL |
@@ -356,8 +364,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_FULL;
                break;
        case SPEED_10:
-               pr_debug("10 Mbps Speed specified without Duplex\n");
-               pr_debug("Using Autonegotiation at 10 Mbps only\n");
+               netdev_dbg(adapter->netdev,
+                          "10 Mbps Speed specified without Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at 10 Mbps only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF |
                                                PHY_ADVERTISE_10_FULL;
@@ -365,22 +375,24 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_10 + HALF_DUPLEX:
-               pr_debug("Forcing to 10 Mbps Half Duplex\n");
+               netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Half Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_10;
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_10 + FULL_DUPLEX:
-               pr_debug("Forcing to 10 Mbps Full Duplex\n");
+               netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Full Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_10;
                hw->mac.link_duplex = DUPLEX_FULL;
                break;
        case SPEED_100:
-               pr_debug("100 Mbps Speed specified without Duplex\n");
-               pr_debug("Using Autonegotiation at 100 Mbps only\n");
+               netdev_dbg(adapter->netdev,
+                          "100 Mbps Speed specified without Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at 100 Mbps only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_100_HALF |
                                                PHY_ADVERTISE_100_FULL;
@@ -388,28 +400,33 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter)
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_100 + HALF_DUPLEX:
-               pr_debug("Forcing to 100 Mbps Half Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Forcing to 100 Mbps Half Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_100;
                hw->mac.link_duplex = DUPLEX_HALF;
                break;
        case SPEED_100 + FULL_DUPLEX:
-               pr_debug("Forcing to 100 Mbps Full Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "Forcing to 100 Mbps Full Duplex\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 0;
                hw->phy.autoneg_advertised = 0;
                hw->mac.link_speed = SPEED_100;
                hw->mac.link_duplex = DUPLEX_FULL;
                break;
        case SPEED_1000:
-               pr_debug("1000 Mbps Speed specified without Duplex\n");
+               netdev_dbg(adapter->netdev,
+                          "1000 Mbps Speed specified without Duplex\n");
                goto full_duplex_only;
        case SPEED_1000 + HALF_DUPLEX:
-               pr_debug("Half Duplex is not supported at 1000 Mbps\n");
+               netdev_dbg(adapter->netdev,
+                          "Half Duplex is not supported at 1000 Mbps\n");
                /* fall through */
        case SPEED_1000 + FULL_DUPLEX:
 full_duplex_only:
-               pr_debug("Using Autonegotiation at 1000 Mbps Full Duplex only\n");
+               netdev_dbg(adapter->netdev,
+                          "Using Autonegotiation at 1000 Mbps Full Duplex only\n");
                hw->mac.autoneg = hw->mac.fc_autoneg = 1;
                hw->phy.autoneg_advertised = PHY_ADVERTISE_1000_FULL;
                hw->mac.link_speed = SPEED_1000;
index 28bb960..da07907 100644 (file)
@@ -97,6 +97,7 @@
  */
 s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw)
 {
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        struct pch_gbe_phy_info *phy = &hw->phy;
        s32 ret;
        u16 phy_id1;
@@ -115,8 +116,9 @@ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw)
        phy->id = (u32)phy_id1;
        phy->id = ((phy->id << 6) | ((phy_id2 & 0xFC00) >> 10));
        phy->revision = (u32) (phy_id2 & 0x000F);
-       pr_debug("phy->id : 0x%08x  phy->revision : 0x%08x\n",
-                phy->id, phy->revision);
+       netdev_dbg(adapter->netdev,
+                  "phy->id : 0x%08x  phy->revision : 0x%08x\n",
+                  phy->id, phy->revision);
        return 0;
 }
 
@@ -134,7 +136,10 @@ s32 pch_gbe_phy_read_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 *data)
        struct pch_gbe_phy_info *phy = &hw->phy;
 
        if (offset > PHY_MAX_REG_ADDRESS) {
-               pr_err("PHY Address %d is out of range\n", offset);
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "PHY Address %d is out of range\n",
+                          offset);
                return -EINVAL;
        }
        *data = pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_READ,
@@ -156,7 +161,10 @@ s32 pch_gbe_phy_write_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 data)
        struct pch_gbe_phy_info *phy = &hw->phy;
 
        if (offset > PHY_MAX_REG_ADDRESS) {
-               pr_err("PHY Address %d is out of range\n", offset);
+               struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
+
+               netdev_err(adapter->netdev, "PHY Address %d is out of range\n",
+                          offset);
                return -EINVAL;
        }
        pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_WRITE,
@@ -235,7 +243,7 @@ void pch_gbe_phy_power_down(struct pch_gbe_hw *hw)
  * pch_gbe_phy_set_rgmii - RGMII interface setting
  * @hw:                    Pointer to the HW structure
  */
-inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
+void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
 {
        pch_gbe_phy_sw_reset(hw);
 }
@@ -246,15 +254,14 @@ inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
  */
 void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw)
 {
-       struct pch_gbe_adapter *adapter;
+       struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
        struct ethtool_cmd     cmd = { .cmd = ETHTOOL_GSET };
        int ret;
        u16 mii_reg;
 
-       adapter = container_of(hw, struct pch_gbe_adapter, hw);
        ret = mii_ethtool_gset(&adapter->mii, &cmd);
        if (ret)
-               pr_err("Error: mii_ethtool_gset\n");
+               netdev_err(adapter->netdev, "Error: mii_ethtool_gset\n");
 
        ethtool_cmd_speed_set(&cmd, hw->mac.link_speed);
        cmd.duplex = hw->mac.link_duplex;
@@ -263,12 +270,11 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw)
        pch_gbe_phy_write_reg_miic(hw, MII_BMCR, BMCR_RESET);
        ret = mii_ethtool_sset(&adapter->mii, &cmd);
        if (ret)
-               pr_err("Error: mii_ethtool_sset\n");
+               netdev_err(adapter->netdev, "Error: mii_ethtool_sset\n");
 
        pch_gbe_phy_sw_reset(hw);
 
        pch_gbe_phy_read_reg_miic(hw, PHY_PHYSP_CONTROL, &mii_reg);
        mii_reg |= PHYSP_CTRL_ASSERT_CRS_TX;
        pch_gbe_phy_write_reg_miic(hw, PHY_PHYSP_CONTROL, mii_reg);
-
 }
index 534e36e..b00cf56 100644 (file)
@@ -38,8 +38,8 @@
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 2
-#define _QLCNIC_LINUX_SUBVERSION 43
-#define QLCNIC_LINUX_VERSIONID  "5.2.43"
+#define _QLCNIC_LINUX_SUBVERSION 44
+#define QLCNIC_LINUX_VERSIONID  "5.2.44"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -393,6 +393,9 @@ struct qlcnic_fw_dump {
        u32     size;   /* total size of the dump */
        void    *data;  /* dump data area */
        struct  qlcnic_dump_template_hdr *tmpl_hdr;
+       dma_addr_t phys_addr;
+       void    *dma_buffer;
+       bool    use_pex_dma;
 };
 
 /*
@@ -426,6 +429,7 @@ struct qlcnic_hardware_context {
        u8 nic_mode;
        char diag_cnt;
 
+       u16 max_uc_count;
        u16 port_type;
        u16 board_type;
        u16 supported_type;
@@ -445,7 +449,7 @@ struct qlcnic_hardware_context {
        u16 max_pci_func;
 
        u32 capabilities;
-       u32 capabilities2;
+       u32 extra_capability[3];
        u32 temp;
        u32 int_vec_bit;
        u32 fw_hal_version;
@@ -815,7 +819,7 @@ struct qlcnic_mac_list_s {
 
 #define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2
 #define QLCNIC_FW_CAP2_HW_LRO_IPV6             BIT_3
-#define QLCNIC_FW_CAPABILITY_2_OCBB            BIT_5
+#define QLCNIC_FW_CAPABILITY_SET_DRV_VER       BIT_5
 #define QLCNIC_FW_CAPABILITY_2_BEACON          BIT_7
 
 /* module types */
@@ -1472,7 +1476,7 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *);
 void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter);
 
 int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);
-int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *);
+int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *, u32);
 int qlcnic_change_mtu(struct net_device *netdev, int new_mtu);
 netdev_features_t qlcnic_fix_features(struct net_device *netdev,
        netdev_features_t features);
@@ -1494,7 +1498,9 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
 int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, size_t);
 int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32);
 void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter);
+void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *);
 int qlcnic_enable_msix(struct qlcnic_adapter *, u32);
+void qlcnic_set_drv_version(struct qlcnic_adapter *);
 
 /*  eSwitch management functions */
 int qlcnic_config_switch_port(struct qlcnic_adapter *,
@@ -1590,6 +1596,8 @@ struct qlcnic_nic_template {
        void (*napi_del)(struct qlcnic_adapter *);
        void (*config_ipaddr)(struct qlcnic_adapter *, __be32, int);
        irqreturn_t (*clear_legacy_intr)(struct qlcnic_adapter *);
+       int (*shutdown)(struct pci_dev *);
+       int (*resume)(struct qlcnic_adapter *);
 };
 
 /* Adapter hardware abstraction */
@@ -1631,6 +1639,7 @@ struct qlcnic_hardware_ops {
        int (*config_promisc_mode) (struct qlcnic_adapter *, u32);
        void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16);
        int (*get_board_info) (struct qlcnic_adapter *);
+       void (*set_mac_filter_count) (struct qlcnic_adapter *);
        void (*free_mac_list) (struct qlcnic_adapter *);
 };
 
@@ -1793,6 +1802,18 @@ static inline void qlcnic_napi_enable(struct qlcnic_adapter *adapter)
        adapter->ahw->hw_ops->napi_enable(adapter);
 }
 
+static inline int __qlcnic_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+
+       return adapter->nic_ops->shutdown(pdev);
+}
+
+static inline int __qlcnic_resume(struct qlcnic_adapter *adapter)
+{
+       return adapter->nic_ops->resume(adapter);
+}
+
 static inline void qlcnic_napi_disable(struct qlcnic_adapter *adapter)
 {
        adapter->ahw->hw_ops->napi_disable(adapter);
@@ -1846,6 +1867,11 @@ static inline void qlcnic_free_mac_list(struct qlcnic_adapter *adapter)
        return adapter->ahw->hw_ops->free_mac_list(adapter);
 }
 
+static inline void qlcnic_set_mac_filter_count(struct qlcnic_adapter *adapter)
+{
+       adapter->ahw->hw_ops->set_mac_filter_count(adapter);
+}
+
 static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter,
                                            u32 key)
 {
index f63a695..0913c62 100644 (file)
@@ -63,6 +63,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_STOP_NIC_FUNC, 2, 1},
        {QLCNIC_CMD_SET_LED_CONFIG, 5, 1},
        {QLCNIC_CMD_GET_LED_CONFIG, 1, 5},
+       {QLCNIC_CMD_83XX_SET_DRV_VER, 4, 1},
        {QLCNIC_CMD_ADD_RCV_RINGS, 130, 26},
        {QLCNIC_CMD_CONFIG_VPORT, 4, 4},
        {QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},
@@ -172,6 +173,7 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {
        .config_promisc_mode            = qlcnic_83xx_nic_set_promisc,
        .change_l2_filter               = qlcnic_83xx_change_l2_filter,
        .get_board_info                 = qlcnic_83xx_get_port_info,
+       .set_mac_filter_count           = qlcnic_83xx_set_mac_filter_count,
        .free_mac_list                  = qlcnic_82xx_free_mac_list,
 };
 
@@ -184,6 +186,8 @@ static struct qlcnic_nic_template qlcnic_83xx_ops = {
        .napi_del               = qlcnic_83xx_napi_del,
        .config_ipaddr          = qlcnic_83xx_config_ipaddr,
        .clear_legacy_intr      = qlcnic_83xx_clear_legacy_intr,
+       .shutdown               = qlcnic_83xx_shutdown,
+       .resume                 = qlcnic_83xx_resume,
 };
 
 void qlcnic_83xx_register_map(struct qlcnic_hardware_context *ahw)
@@ -609,6 +613,22 @@ int qlcnic_83xx_get_port_info(struct qlcnic_adapter *adapter)
        return status;
 }
 
+void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u16 act_pci_fn = ahw->act_pci_func;
+       u16 count;
+
+       ahw->max_mc_count = QLC_83XX_MAX_MC_COUNT;
+       if (act_pci_fn <= 2)
+               count = (QLC_83XX_MAX_UC_COUNT - QLC_83XX_MAX_MC_COUNT) /
+                        act_pci_fn;
+       else
+               count = (QLC_83XX_LB_MAX_FILTERS - QLC_83XX_MAX_MC_COUNT) /
+                        act_pci_fn;
+       ahw->max_uc_count = count;
+}
+
 void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *adapter)
 {
        u32 val;
@@ -844,7 +864,9 @@ void qlcnic_83xx_idc_aen_work(struct work_struct *work)
        int i, err = 0;
 
        adapter = container_of(work, struct qlcnic_adapter, idc_aen_work.work);
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_IDC_ACK);
+       if (err)
+               return;
 
        for (i = 1; i < QLC_83XX_MBX_AEN_CNT; i++)
                cmd.req.arg[i] = adapter->ahw->mbox_aen[i];
@@ -1085,8 +1107,10 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)
                cap |= QLC_83XX_FW_CAP_LRO_MSS;
 
        /* set mailbox hdr and capabilities */
-       qlcnic_alloc_mbx_args(&cmd, adapter,
-                             QLCNIC_CMD_CREATE_RX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CREATE_RX_CTX);
+       if (err)
+               return err;
 
        if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
                cmd.req.arg[0] |= (0x3 << 29);
@@ -1244,7 +1268,9 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,
                mbx.intr_id = 0xffff;
        mbx.src = 0;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       if (err)
+               return err;
 
        if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter))
                cmd.req.arg[0] |= (0x3 << 29);
@@ -1390,8 +1416,11 @@ int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state,
 
        if (state) {
                /* Get LED configuration */
-               qlcnic_alloc_mbx_args(&cmd, adapter,
-                                     QLCNIC_CMD_GET_LED_CONFIG);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_GET_LED_CONFIG);
+               if (status)
+                       return status;
+
                status = qlcnic_issue_cmd(adapter, &cmd);
                if (status) {
                        dev_err(&adapter->pdev->dev,
@@ -1405,8 +1434,11 @@ int qlcnic_83xx_config_led(struct qlcnic_adapter *adapter, u32 state,
                /* Set LED Configuration */
                mbx_in = (LSW(QLC_83XX_LED_CONFIG) << 16) |
                          LSW(QLC_83XX_LED_CONFIG);
-               qlcnic_alloc_mbx_args(&cmd, adapter,
-                                     QLCNIC_CMD_SET_LED_CONFIG);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_SET_LED_CONFIG);
+               if (status)
+                       return status;
+
                cmd.req.arg[1] = mbx_in;
                cmd.req.arg[2] = mbx_in;
                cmd.req.arg[3] = mbx_in;
@@ -1423,8 +1455,11 @@ mbx_err:
 
        } else {
                /* Restoring default LED configuration */
-               qlcnic_alloc_mbx_args(&cmd, adapter,
-                                     QLCNIC_CMD_SET_LED_CONFIG);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_SET_LED_CONFIG);
+               if (status)
+                       return status;
+
                cmd.req.arg[1] = adapter->ahw->mbox_reg[0];
                cmd.req.arg[2] = adapter->ahw->mbox_reg[1];
                cmd.req.arg[3] = adapter->ahw->mbox_reg[2];
@@ -1494,10 +1529,18 @@ void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *adapter,
                return;
 
        if (enable) {
-               qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INIT_NIC_FUNC);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_INIT_NIC_FUNC);
+               if (status)
+                       return;
+
                cmd.req.arg[1] = BIT_0 | BIT_31;
        } else {
-               qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_STOP_NIC_FUNC);
+               if (status)
+                       return;
+
                cmd.req.arg[1] = BIT_0 | BIT_31;
        }
        status = qlcnic_issue_cmd(adapter, &cmd);
@@ -1514,7 +1557,10 @@ int qlcnic_83xx_set_port_config(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        int err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORT_CONFIG);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = adapter->ahw->port_config;
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
@@ -1528,7 +1574,10 @@ int qlcnic_83xx_get_port_config(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        int err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PORT_CONFIG);
+       if (err)
+               return err;
+
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
                dev_info(&adapter->pdev->dev, "Get Port config failed\n");
@@ -1544,7 +1593,10 @@ int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable)
        u32 temp;
        struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_EVENT);
+       if (err)
+               return err;
+
        temp = adapter->recv_ctx->context_id << 16;
        cmd.req.arg[1] = (enable ? 1 : 0) | BIT_8 | temp;
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -1575,7 +1627,11 @@ int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return -EIO;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_MAC_RX_MODE);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CONFIGURE_MAC_RX_MODE);
+       if (err)
+               return err;
+
        qlcnic_83xx_set_interface_id_promisc(adapter, &temp);
        cmd.req.arg[1] = (mode ? 1 : 0) | temp;
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -1623,13 +1679,19 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)
 
        /* Poll for link up event before running traffic */
        do {
-               msleep(500);
+               msleep(QLC_83XX_LB_MSLEEP_COUNT);
                if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
                        qlcnic_83xx_process_aen(adapter);
 
-               if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
-                       dev_info(&adapter->pdev->dev,
-                                "Firmware didn't sent link up event to loopback request\n");
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+                       netdev_info(netdev,
+                                   "Device is resetting, free LB test resources\n");
+                       ret = -EIO;
+                       goto free_diag_res;
+               }
+               if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
+                       netdev_info(netdev,
+                                   "Firmware didn't sent link up event to loopback request\n");
                        ret = -QLCNIC_FW_NOT_RESPOND;
                        qlcnic_83xx_clear_lb_mode(adapter, mode);
                        goto free_diag_res;
@@ -1658,6 +1720,7 @@ fail_diag_alloc:
 int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct net_device *netdev = adapter->netdev;
        int status = 0, loop = 0;
        u32 config;
 
@@ -1675,9 +1738,9 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        status = qlcnic_83xx_set_port_config(adapter);
        if (status) {
-               dev_err(&adapter->pdev->dev,
-                       "Failed to Set Loopback Mode = 0x%x.\n",
-                       ahw->port_config);
+               netdev_err(netdev,
+                          "Failed to Set Loopback Mode = 0x%x.\n",
+                          ahw->port_config);
                ahw->port_config = config;
                clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                return status;
@@ -1685,13 +1748,19 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        /* Wait for Link and IDC Completion AEN */
        do {
-               msleep(300);
+               msleep(QLC_83XX_LB_MSLEEP_COUNT);
                if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
                        qlcnic_83xx_process_aen(adapter);
 
-               if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
-                       dev_err(&adapter->pdev->dev,
-                               "FW did not generate IDC completion AEN\n");
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+                       netdev_info(netdev,
+                                   "Device is resetting, free LB test resources\n");
+                       clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
+                       return -EIO;
+               }
+               if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
+                       netdev_err(netdev,
+                                  "Did not receive IDC completion AEN\n");
                        clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                        qlcnic_83xx_clear_lb_mode(adapter, mode);
                        return -EIO;
@@ -1706,6 +1775,7 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct net_device *netdev = adapter->netdev;
        int status = 0, loop = 0;
        u32 config = ahw->port_config;
 
@@ -1717,9 +1787,9 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        status = qlcnic_83xx_set_port_config(adapter);
        if (status) {
-               dev_err(&adapter->pdev->dev,
-                       "Failed to Clear Loopback Mode = 0x%x.\n",
-                       ahw->port_config);
+               netdev_err(netdev,
+                          "Failed to Clear Loopback Mode = 0x%x.\n",
+                          ahw->port_config);
                ahw->port_config = config;
                clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                return status;
@@ -1727,13 +1797,20 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)
 
        /* Wait for Link and IDC Completion AEN */
        do {
-               msleep(300);
+               msleep(QLC_83XX_LB_MSLEEP_COUNT);
                if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
                        qlcnic_83xx_process_aen(adapter);
 
-               if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {
-                       dev_err(&adapter->pdev->dev,
-                               "Firmware didn't sent IDC completion AEN\n");
+               if (test_bit(__QLCNIC_RESETTING, &adapter->state)) {
+                       netdev_info(netdev,
+                                   "Device is resetting, free LB test resources\n");
+                       clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
+                       return -EIO;
+               }
+
+               if (loop++ > QLC_83XX_LB_WAIT_COUNT) {
+                       netdev_err(netdev,
+                                  "Did not receive IDC completion AEN\n");
                        clear_bit(QLC_83XX_IDC_COMP_AEN, &ahw->idc.status);
                        return -EIO;
                }
@@ -1762,7 +1839,11 @@ void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,
        u32 temp = 0, temp_ip;
        struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_IP_ADDR);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CONFIGURE_IP_ADDR);
+       if (err)
+               return;
+
        qlcnic_83xx_set_interface_id_ipaddr(adapter, &temp);
 
        if (mode == QLCNIC_IP_UP)
@@ -1801,7 +1882,10 @@ int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *adapter, int mode)
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return 0;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_HW_LRO);
+       if (err)
+               return err;
+
        temp = adapter->recv_ctx->context_id << 16;
        arg1 = lro_bit_mask | temp;
        cmd.req.arg[1] = arg1;
@@ -1823,8 +1907,9 @@ int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable)
                            0xae7b30b4d0ca2bcbULL, 0x43a38fb04167253dULL,
                            0x255b0ec26d5a56daULL };
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS);
-
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_RSS);
+       if (err)
+               return err;
        /*
         * RSS request:
         * bits 3-0: Rsvd
@@ -1930,7 +2015,10 @@ int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
        struct qlcnic_cmd_args cmd;
        u32 mac_low, mac_high;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       if (err)
+               return err;
+
        qlcnic_83xx_configure_mac(adapter, mac, QLCNIC_GET_CURRENT_MAC, &cmd);
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -1961,7 +2049,10 @@ void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *adapter)
        if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)
                return;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTR_COAL);
+       if (err)
+               return;
+
        if (coal->type == QLCNIC_INTR_COAL_TYPE_RX) {
                temp = adapter->recv_ctx->context_id;
                cmd.req.arg[1] = QLCNIC_INTR_COAL_TYPE_RX | temp << 16;
@@ -2033,7 +2124,10 @@ int qlcnic_enable_eswitch(struct qlcnic_adapter *adapter, u8 port, u8 enable)
                return err;
        }
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = (port & 0xf) | BIT_4;
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -2061,7 +2155,10 @@ int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *adapter,
                return err;
        }
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = (nic->pci_func << 16);
        cmd.req.arg[2] = 0x1 << 16;
        cmd.req.arg[3] = nic->phys_port | (nic->switch_mode << 16);
@@ -2092,13 +2189,17 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter,
        u32 temp;
        u8 op = 0;
        struct qlcnic_cmd_args cmd;
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       if (err)
+               return err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
-       if (func_id != adapter->ahw->pci_func) {
+       if (func_id != ahw->pci_func) {
                temp = func_id << 16;
                cmd.req.arg[1] = op | BIT_31 | temp;
        } else {
-               cmd.req.arg[1] = adapter->ahw->pci_func << 16;
+               cmd.req.arg[1] = ahw->pci_func << 16;
        }
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err) {
@@ -2125,6 +2226,9 @@ int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *adapter,
                temp = (cmd.rsp.arg[8] & 0x7FFE0000) >> 17;
                npar_info->max_linkspeed_reg_offset = temp;
        }
+       if (npar_info->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS)
+               memcpy(ahw->extra_capability, &cmd.rsp.arg[16],
+                      sizeof(ahw->extra_capability));
 
 out:
        qlcnic_free_mbx_args(&cmd);
@@ -2140,7 +2244,10 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,
        int i, err = 0, j = 0;
        u32 temp;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       if (err)
+               return err;
+
        err = qlcnic_issue_cmd(adapter, &cmd);
 
        ahw->act_pci_func = 0;
@@ -2195,7 +2302,10 @@ int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type)
        struct qlcnic_cmd_args cmd;
 
        max_ints = adapter->ahw->num_msix - 1;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = max_ints;
 
        if (qlcnic_sriov_vf_check(adapter))
@@ -2823,7 +2933,11 @@ int qlcnic_83xx_test_link(struct qlcnic_adapter *adapter)
                dev_info(&adapter->pdev->dev, "link state down\n");
                return config;
        }
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_STATUS);
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LINK_STATUS);
+       if (err)
+               return err;
+
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err) {
                dev_info(&adapter->pdev->dev,
@@ -3049,7 +3163,9 @@ void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data)
        struct net_device *netdev = adapter->netdev;
        int ret = 0;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_STATISTICS);
+       if (ret)
+               return;
        /* Get Tx stats */
        cmd.req.arg[1] = BIT_1 | (adapter->tx_ring->ctx_id << 16);
        cmd.rsp.num = QLC_83XX_TX_STAT_REGS;
@@ -3139,7 +3255,9 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
                goto fail_diag_irq;
 
        ahw->diag_cnt = 0;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       if (ret)
+               goto fail_diag_irq;
 
        if (adapter->flags & QLCNIC_MSIX_ENABLED)
                intrpt_id = ahw->intr_tbl[0].id;
@@ -3277,3 +3395,54 @@ int qlcnic_83xx_flash_test(struct qlcnic_adapter *adapter)
        }
        return 0;
 }
+
+int qlcnic_83xx_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+       int retval;
+
+       netif_device_detach(netdev);
+       qlcnic_cancel_idc_work(adapter);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_83xx_disable_mbx_intr(adapter);
+       cancel_delayed_work_sync(&adapter->idc_aen_work);
+
+       retval = pci_save_state(pdev);
+       if (retval)
+               return retval;
+
+       return 0;
+}
+
+int qlcnic_83xx_resume(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlc_83xx_idc *idc = &ahw->idc;
+       int err = 0;
+
+       err = qlcnic_83xx_idc_init(adapter);
+       if (err)
+               return err;
+
+       if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) {
+               if (ahw->op_mode == QLCNIC_MGMT_FUNC) {
+                       qlcnic_83xx_set_vnic_opmode(adapter);
+               } else {
+                       err = qlcnic_83xx_check_vnic_state(adapter);
+                       if (err)
+                               return err;
+               }
+       }
+
+       err = qlcnic_83xx_idc_reattach_driver(adapter);
+       if (err)
+               return err;
+
+       qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state,
+                            idc->delay);
+       return err;
+}
index 1bfe283..2548d14 100644 (file)
@@ -36,7 +36,8 @@
 #define QLC_83XX_MAX_DRV_LOCK_RECOVERY_ATTEMPT         3
 #define QLC_83XX_DRV_LOCK_RECOVERY_DELAY               200
 #define QLC_83XX_DRV_LOCK_RECOVERY_STATUS_MASK         0x3
-
+#define QLC_83XX_LB_WAIT_COUNT                         250
+#define QLC_83XX_LB_MSLEEP_COUNT                       20
 #define QLC_83XX_NO_NIC_RESOURCE       0x5
 #define QLC_83XX_MAC_PRESENT           0xC
 #define QLC_83XX_MAC_ABSENT            0xD
@@ -393,6 +394,8 @@ enum qlcnic_83xx_states {
 #define QLC_83XX_LB_MAX_FILTERS                        2048
 #define QLC_83XX_LB_BUCKET_SIZE                        256
 #define QLC_83XX_MINIMUM_VECTOR                        3
+#define QLC_83XX_MAX_MC_COUNT                  38
+#define QLC_83XX_MAX_UC_COUNT                  4096
 
 #define QLC_83XX_GET_FUNC_MODE_FROM_NPAR_INFO(val)     (val & 0x80000000)
 #define QLC_83XX_GET_LRO_CAPABILITY(val)               (val & 0x20)
@@ -624,4 +627,11 @@ u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *);
 u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *, u32 *);
 void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *);
 void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *);
+void qlcnic_83xx_set_mac_filter_count(struct qlcnic_adapter *);
+int qlcnic_83xx_shutdown(struct pci_dev *);
+int qlcnic_83xx_resume(struct qlcnic_adapter *);
+int qlcnic_83xx_idc_init(struct qlcnic_adapter *);
+int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *);
+int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *);
+int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *);
 #endif
index aa26250..f41dfab 100644 (file)
@@ -606,7 +606,7 @@ static int qlcnic_83xx_idc_check_fan_failure(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
 {
        int err;
 
@@ -629,6 +629,7 @@ static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)
                return -EIO;
        }
 
+       qlcnic_set_drv_version(adapter);
        qlcnic_83xx_idc_attach_driver(adapter);
 
        return 0;
@@ -1134,7 +1135,7 @@ qlcnic_83xx_idc_first_to_load_function_handler(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_idc_init(struct qlcnic_adapter *adapter)
 {
        int ret = -EIO;
 
@@ -1553,9 +1554,18 @@ static int qlcnic_83xx_reset_template_checksum(struct qlcnic_adapter *p_dev)
 
 int qlcnic_83xx_get_reset_instruction_template(struct qlcnic_adapter *p_dev)
 {
-       u8 *p_buff;
-       u32 addr, count;
        struct qlcnic_hardware_context *ahw = p_dev->ahw;
+       u32 addr, count, prev_ver, curr_ver;
+       u8 *p_buff;
+
+       if (ahw->reset.buff != NULL) {
+               prev_ver = p_dev->fw_version;
+               curr_ver = qlcnic_83xx_get_fw_version(p_dev);
+               if (curr_ver > prev_ver)
+                       kfree(ahw->reset.buff);
+               else
+                       return 0;
+       }
 
        ahw->reset.seq_error = 0;
        ahw->reset.buff = kzalloc(QLC_83XX_RESTART_TEMPLATE_SIZE, GFP_KERNEL);
@@ -2083,7 +2093,11 @@ static void qlcnic_83xx_clear_function_resources(struct qlcnic_adapter *adapter)
        audit_mask = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT);
 
        if (IS_QLC_83XX_USED(adapter, presence_mask, audit_mask)) {
-               qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_STOP_NIC_FUNC);
+               status = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                              QLCNIC_CMD_STOP_NIC_FUNC);
+               if (status)
+                       return;
+
                cmd.req.arg[1] = BIT_31;
                status = qlcnic_issue_cmd(adapter, &cmd);
                if (status)
index b5054e1..599d1fd 100644 (file)
@@ -39,7 +39,7 @@ int qlcnic_83xx_disable_vnic_mode(struct qlcnic_adapter *adapter, int lock)
        return 0;
 }
 
-static int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter)
 {
        u8 id;
        int ret = -EBUSY;
@@ -218,3 +218,24 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter)
 
        return 0;
 }
+
+int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       struct qlc_83xx_idc *idc = &ahw->idc;
+       u32 state;
+
+       state = QLCRDX(ahw, QLC_83XX_VNIC_STATE);
+       while (state != QLCNIC_DEV_NPAR_OPER && idc->vnic_wait_limit--) {
+               msleep(1000);
+               state = QLCRDX(ahw, QLC_83XX_VNIC_STATE);
+       }
+
+       if (!idc->vnic_wait_limit) {
+               dev_err(&adapter->pdev->dev,
+                       "vNIC mode not operational, state check timed out.\n");
+               return -EIO;
+       }
+
+       return 0;
+}
index 9d0ae11..0d54fce 100644 (file)
@@ -36,7 +36,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = {
        {QLCNIC_CMD_CONFIG_PORT, 4, 1},
        {QLCNIC_CMD_TEMP_SIZE, 4, 4},
        {QLCNIC_CMD_GET_TEMP_HDR, 4, 1},
-       {QLCNIC_CMD_SET_DRV_VER, 4, 1},
+       {QLCNIC_CMD_82XX_SET_DRV_VER, 4, 1},
        {QLCNIC_CMD_GET_LED_STATUS, 4, 2},
 };
 
@@ -182,7 +182,7 @@ int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
        return cmd->rsp.arg[0];
 }
 
-int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter)
+int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter, u32 fw_cmd)
 {
        struct qlcnic_cmd_args cmd;
        u32 arg1, arg2, arg3;
@@ -194,7 +194,10 @@ int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter)
                 _QLCNIC_LINUX_MAJOR, _QLCNIC_LINUX_MINOR,
                 _QLCNIC_LINUX_SUBVERSION);
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_DRV_VER);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, fw_cmd);
+       if (err)
+               return err;
+
        memcpy(&arg1, drv_string, sizeof(u32));
        memcpy(&arg2, drv_string + 4, sizeof(u32));
        memcpy(&arg3, drv_string + 8, sizeof(u32));
@@ -222,7 +225,10 @@ qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
 
        if (recv_ctx->state != QLCNIC_HOST_CTX_STATE_ACTIVE)
                return err;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_MTU);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = recv_ctx->context_id;
        cmd.req.arg[2] = mtu;
 
@@ -336,7 +342,10 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
        }
 
        phys_addr = hostrq_phys_addr;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_RX_CTX);
+       if (err)
+               goto out_free_rsp;
+
        cmd.req.arg[1] = MSD(phys_addr);
        cmd.req.arg[2] = LSD(phys_addr);
        cmd.req.arg[3] = rq_size;
@@ -374,10 +383,10 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
        recv_ctx->context_id = le16_to_cpu(prsp->context_id);
        recv_ctx->virt_port = prsp->virt_port;
 
+       qlcnic_free_mbx_args(&cmd);
 out_free_rsp:
        dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp,
-               cardrsp_phys_addr);
-       qlcnic_free_mbx_args(&cmd);
+                         cardrsp_phys_addr);
 out_free_rq:
        dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr);
        return err;
@@ -389,7 +398,10 @@ void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX);
+       if (err)
+               return;
+
        cmd.req.arg[1] = recv_ctx->context_id;
        err = qlcnic_issue_cmd(adapter, &cmd);
        if (err)
@@ -458,7 +470,10 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
 
        phys_addr = rq_phys_addr;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX);
+       if (err)
+               goto out_free_rsp;
+
        cmd.req.arg[1] = MSD(phys_addr);
        cmd.req.arg[2] = LSD(phys_addr);
        cmd.req.arg[3] = rq_size;
@@ -474,12 +489,13 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,
                err = -EIO;
        }
 
+       qlcnic_free_mbx_args(&cmd);
+
+out_free_rsp:
        dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr,
                          rsp_phys_addr);
-
 out_free_rq:
        dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr);
-       qlcnic_free_mbx_args(&cmd);
 
        return err;
 }
@@ -488,8 +504,11 @@ void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter,
                                   struct qlcnic_host_tx_ring *tx_ring)
 {
        struct qlcnic_cmd_args cmd;
+       int ret;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX);
+       if (ret)
+               return;
 
        cmd.req.arg[1] = tx_ring->ctx_id;
        if (qlcnic_issue_cmd(adapter, &cmd))
@@ -504,7 +523,10 @@ qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config)
        int err;
        struct qlcnic_cmd_args cmd;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_PORT);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = config;
        err = qlcnic_issue_cmd(adapter, &cmd);
        qlcnic_free_mbx_args(&cmd);
@@ -708,7 +730,10 @@ int qlcnic_82xx_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac)
        struct qlcnic_cmd_args cmd;
        u32 mac_low, mac_high;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_MAC_ADDRESS);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = adapter->ahw->pci_func | BIT_8;
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -747,7 +772,10 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
 
        nic_info = nic_info_addr;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = MSD(nic_dma_t);
        cmd.req.arg[2] = LSD(nic_dma_t);
        cmd.req.arg[3] = (func_id << 16 | nic_size);
@@ -769,9 +797,10 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,
                npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu);
        }
 
+       qlcnic_free_mbx_args(&cmd);
+out_free_dma:
        dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
                          nic_dma_t);
-       qlcnic_free_mbx_args(&cmd);
 
        return err;
 }
@@ -808,7 +837,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,
        nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw);
        nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw);
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = MSD(nic_dma_t);
        cmd.req.arg[2] = LSD(nic_dma_t);
        cmd.req.arg[3] = ((nic->pci_func << 16) | nic_size);
@@ -820,9 +852,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,
                err = -EIO;
        }
 
-       dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
-               nic_dma_t);
        qlcnic_free_mbx_args(&cmd);
+out_free_dma:
+       dma_free_coherent(&adapter->pdev->dev, nic_size, nic_info_addr,
+                         nic_dma_t);
 
        return err;
 }
@@ -846,7 +879,10 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,
                return -ENOMEM;
 
        npar = pci_info_addr;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = MSD(pci_info_dma_t);
        cmd.req.arg[2] = LSD(pci_info_dma_t);
        cmd.req.arg[3] = pci_size;
@@ -874,9 +910,10 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,
                err = -EIO;
        }
 
+       qlcnic_free_mbx_args(&cmd);
+out_free_dma:
        dma_free_coherent(&adapter->pdev->dev, pci_size, pci_info_addr,
                pci_info_dma_t);
-       qlcnic_free_mbx_args(&cmd);
 
        return err;
 }
@@ -897,7 +934,11 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
        arg1 = id | (enable_mirroring ? BIT_4 : 0);
        arg1 |= pci_func << 8;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_PORTMIRRORING);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_SET_PORTMIRRORING);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = arg1;
        err = qlcnic_issue_cmd(adapter, &cmd);
 
@@ -941,7 +982,11 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
        arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;
        arg1 |= rx_tx << 15 | stats_size << 16;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_STATS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_GET_ESWITCH_STATS);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = arg1;
        cmd.req.arg[2] = MSD(stats_dma_t);
        cmd.req.arg[3] = LSD(stats_dma_t);
@@ -963,9 +1008,10 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
                esw_stats->numbytes = le64_to_cpu(stats->numbytes);
        }
 
-       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
-               stats_dma_t);
        qlcnic_free_mbx_args(&cmd);
+out_free_dma:
+       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
+                         stats_dma_t);
 
        return err;
 }
@@ -989,7 +1035,10 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
        if (!stats_addr)
                return -ENOMEM;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);
+       if (err)
+               goto out_free_dma;
+
        cmd.req.arg[1] = stats_size << 16;
        cmd.req.arg[2] = MSD(stats_dma_t);
        cmd.req.arg[3] = LSD(stats_dma_t);
@@ -1020,11 +1069,12 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
                        "%s: Get mac stats failed, err=%d.\n", __func__, err);
        }
 
-       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
-               stats_dma_t);
-
        qlcnic_free_mbx_args(&cmd);
 
+out_free_dma:
+       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
+                         stats_dma_t);
+
        return err;
 }
 
@@ -1108,7 +1158,11 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw,
        arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12;
        arg1 |= BIT_14 | rx_tx << 15;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_STATS);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_GET_ESWITCH_STATS);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = arg1;
        err = qlcnic_issue_cmd(adapter, &cmd);
        qlcnic_free_mbx_args(&cmd);
@@ -1127,10 +1181,13 @@ static int __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter,
        struct device *dev = &adapter->pdev->dev;
        struct qlcnic_cmd_args cmd;
        u8 pci_func = *arg1 >> 8;
-       int err = -EIO;
+       int err;
+
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG);
+       if (err)
+               return err;
 
-       qlcnic_alloc_mbx_args(&cmd, adapter,
-                             QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG);
        cmd.req.arg[1] = *arg1;
        err = qlcnic_issue_cmd(adapter, &cmd);
        *arg1 = cmd.rsp.arg[1];
@@ -1208,7 +1265,11 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
                return err;
        }
 
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_ESWITCH);
+       err = qlcnic_alloc_mbx_args(&cmd, adapter,
+                                   QLCNIC_CMD_CONFIGURE_ESWITCH);
+       if (err)
+               return err;
+
        cmd.req.arg[1] = arg1;
        cmd.req.arg[2] = arg2;
        err = qlcnic_issue_cmd(adapter, &cmd);
index f67652d..700a463 100644 (file)
@@ -846,7 +846,9 @@ static int qlcnic_irq_test(struct net_device *netdev)
                goto clear_diag_irq;
 
        ahw->diag_cnt = 0;
-       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
+       if (ret)
+               goto free_diag_res;
 
        cmd.req.arg[1] = ahw->pci_func;
        ret = qlcnic_issue_cmd(adapter, &cmd);
@@ -858,6 +860,8 @@ static int qlcnic_irq_test(struct net_device *netdev)
 
 done:
        qlcnic_free_mbx_args(&cmd);
+
+free_diag_res:
        qlcnic_diag_free_res(netdev, max_sds_rings);
 
 clear_diag_irq:
index c0f0c0d..d262211 100644 (file)
@@ -672,6 +672,7 @@ enum {
 #define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT     10
 
 #define QLCNIC_MAX_MC_COUNT            38
+#define QLCNIC_MAX_UC_COUNT            512
 #define QLCNIC_WATCHDOG_TIMEOUTVALUE   5
 
 #define        ISR_MSI_INT_TRIGGER(FUNC) (QLCNIC_PCIX_PS_REG(PCIX_MSI_F(FUNC)))
index 218978d..5b5d2ed 100644 (file)
@@ -499,6 +499,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan)
 void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
        struct netdev_hw_addr *ha;
        static const u8 bcast_addr[ETH_ALEN] = {
                0xff, 0xff, 0xff, 0xff, 0xff, 0xff
@@ -515,25 +516,30 @@ void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
        if (netdev->flags & IFF_PROMISC) {
                if (!(adapter->flags & QLCNIC_PROMISC_DISABLED))
                        mode = VPORT_MISS_MODE_ACCEPT_ALL;
-               goto send_fw_cmd;
-       }
-
-       if ((netdev->flags & IFF_ALLMULTI) ||
-           (netdev_mc_count(netdev) > adapter->ahw->max_mc_count)) {
-               mode = VPORT_MISS_MODE_ACCEPT_MULTI;
-               goto send_fw_cmd;
+       } else if (netdev->flags & IFF_ALLMULTI) {
+               if (netdev_mc_count(netdev) > ahw->max_mc_count) {
+                       mode = VPORT_MISS_MODE_ACCEPT_MULTI;
+               } else if (!netdev_mc_empty(netdev) &&
+                          !qlcnic_sriov_vf_check(adapter)) {
+                               netdev_for_each_mc_addr(ha, netdev)
+                                       qlcnic_nic_add_mac(adapter, ha->addr,
+                                                          vlan);
+               }
+               if (mode != VPORT_MISS_MODE_ACCEPT_MULTI &&
+                   qlcnic_sriov_vf_check(adapter))
+                       qlcnic_vf_add_mc_list(netdev, vlan);
        }
 
-       if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) {
-               netdev_for_each_mc_addr(ha, netdev) {
+       /* configure unicast MAC address, if there is not sufficient space
+        * to store all the unicast addresses then enable promiscuous mode
+        */
+       if (netdev_uc_count(netdev) > ahw->max_uc_count) {
+               mode = VPORT_MISS_MODE_ACCEPT_ALL;
+       } else if (!netdev_uc_empty(netdev)) {
+               netdev_for_each_uc_addr(ha, netdev)
                        qlcnic_nic_add_mac(adapter, ha->addr, vlan);
-               }
        }
 
-       if (qlcnic_sriov_vf_check(adapter))
-               qlcnic_vf_add_mc_list(netdev, vlan);
-
-send_fw_cmd:
        if (!qlcnic_sriov_vf_check(adapter)) {
                if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
                    !adapter->fdb_mac_learn) {
@@ -780,7 +786,8 @@ int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
        word = 0;
        if (enable) {
                word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK;
-               if (adapter->ahw->capabilities2 & QLCNIC_FW_CAP2_HW_LRO_IPV6)
+               if (adapter->ahw->extra_capability[0] &
+                   QLCNIC_FW_CAP2_HW_LRO_IPV6)
                        word |= QLCNIC_ENABLE_IPV6_LRO |
                                QLCNIC_NO_DEST_IPV6_CHECK;
        }
@@ -1570,3 +1577,54 @@ void qlcnic_82xx_api_unlock(struct qlcnic_adapter *adapter)
 {
        qlcnic_pcie_sem_unlock(adapter, 5);
 }
+
+int qlcnic_82xx_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+       int retval;
+
+       netif_device_detach(netdev);
+
+       qlcnic_cancel_idc_work(adapter);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_clr_all_drv_state(adapter, 0);
+
+       clear_bit(__QLCNIC_RESETTING, &adapter->state);
+
+       retval = pci_save_state(pdev);
+       if (retval)
+               return retval;
+
+       if (qlcnic_wol_supported(adapter)) {
+               pci_enable_wake(pdev, PCI_D3cold, 1);
+               pci_enable_wake(pdev, PCI_D3hot, 1);
+       }
+
+       return 0;
+}
+
+int qlcnic_82xx_resume(struct qlcnic_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       int err;
+
+       err = qlcnic_start_firmware(adapter);
+       if (err) {
+               dev_err(&adapter->pdev->dev, "failed to start firmware\n");
+               return err;
+       }
+
+       if (netif_running(netdev)) {
+               err = qlcnic_up(adapter, netdev);
+               if (!err)
+                       qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+       }
+
+       netif_device_attach(netdev);
+       qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
+       return err;
+}
index 812fd07..2c22504 100644 (file)
@@ -86,7 +86,7 @@ enum qlcnic_regs {
 #define QLCNIC_CMD_BC_EVENT_SETUP              0x31
 #define        QLCNIC_CMD_CONFIG_VPORT                 0x32
 #define QLCNIC_CMD_GET_MAC_STATS               0x37
-#define QLCNIC_CMD_SET_DRV_VER                 0x38
+#define QLCNIC_CMD_82XX_SET_DRV_VER            0x38
 #define QLCNIC_CMD_GET_LED_STATUS              0x3C
 #define QLCNIC_CMD_CONFIGURE_RSS               0x41
 #define QLCNIC_CMD_CONFIG_INTR_COAL            0x43
@@ -103,6 +103,7 @@ enum qlcnic_regs {
 #define QLCNIC_CMD_GET_LINK_STATUS             0x68
 #define QLCNIC_CMD_SET_LED_CONFIG              0x69
 #define QLCNIC_CMD_GET_LED_CONFIG              0x6A
+#define QLCNIC_CMD_83XX_SET_DRV_VER            0x6F
 #define QLCNIC_CMD_ADD_RCV_RINGS               0x0B
 
 #define QLCNIC_INTRPT_INTX                     1
@@ -198,4 +199,8 @@ void qlcnic_82xx_api_unlock(struct qlcnic_adapter *);
 void qlcnic_82xx_napi_enable(struct qlcnic_adapter *);
 void qlcnic_82xx_napi_disable(struct qlcnic_adapter *);
 void qlcnic_82xx_napi_del(struct qlcnic_adapter *);
+int qlcnic_82xx_shutdown(struct pci_dev *);
+int qlcnic_82xx_resume(struct qlcnic_adapter *);
+void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed);
+void qlcnic_fw_poll_work(struct work_struct *work);
 #endif                         /* __QLCNIC_HW_H_ */
index 0ae8835..4528f8e 100644 (file)
@@ -59,13 +59,11 @@ static int qlcnic_close(struct net_device *netdev);
 static void qlcnic_tx_timeout(struct net_device *netdev);
 static void qlcnic_attach_work(struct work_struct *work);
 static void qlcnic_fwinit_work(struct work_struct *work);
-static void qlcnic_fw_poll_work(struct work_struct *work);
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void qlcnic_poll_controller(struct net_device *netdev);
 #endif
 
 static void qlcnic_idc_debug_info(struct qlcnic_adapter *adapter, u8 encoding);
-static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8);
 static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter);
 
 static irqreturn_t qlcnic_tmp_intr(int irq, void *data);
@@ -360,12 +358,15 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
                return ndo_dflt_fdb_del(ndm, tb, netdev, addr);
 
        if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
-               if (is_unicast_ether_addr(addr))
-                       err = qlcnic_nic_del_mac(adapter, addr);
-               else if (is_multicast_ether_addr(addr))
+               if (is_unicast_ether_addr(addr)) {
+                       err = dev_uc_del(netdev, addr);
+                       if (!err)
+                               err = qlcnic_nic_del_mac(adapter, addr);
+               } else if (is_multicast_ether_addr(addr)) {
                        err = dev_mc_del(netdev, addr);
-               else
+               } else {
                        err =  -EINVAL;
+               }
        }
        return err;
 }
@@ -388,12 +389,16 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        if (ether_addr_equal(addr, adapter->mac_addr))
                return err;
 
-       if (is_unicast_ether_addr(addr))
-               err = qlcnic_nic_add_mac(adapter, addr, 0);
-       else if (is_multicast_ether_addr(addr))
+       if (is_unicast_ether_addr(addr)) {
+               if (netdev_uc_count(netdev) < adapter->ahw->max_uc_count)
+                       err = dev_uc_add_excl(netdev, addr);
+               else
+                       err = -ENOMEM;
+       } else if (is_multicast_ether_addr(addr)) {
                err = dev_mc_add_excl(netdev, addr);
-       else
+       } else {
                err = -EINVAL;
+       }
 
        return err;
 }
@@ -462,6 +467,8 @@ static struct qlcnic_nic_template qlcnic_ops = {
        .napi_add               = qlcnic_82xx_napi_add,
        .napi_del               = qlcnic_82xx_napi_del,
        .config_ipaddr          = qlcnic_82xx_config_ipaddr,
+       .shutdown               = qlcnic_82xx_shutdown,
+       .resume                 = qlcnic_82xx_resume,
        .clear_legacy_intr      = qlcnic_82xx_clear_legacy_intr,
 };
 
@@ -505,6 +512,7 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {
        .config_promisc_mode            = qlcnic_82xx_nic_set_promisc,
        .change_l2_filter               = qlcnic_82xx_change_filter,
        .get_board_info                 = qlcnic_82xx_get_board_info,
+       .set_mac_filter_count           = qlcnic_82xx_set_mac_filter_count,
        .free_mac_list                  = qlcnic_82xx_free_mac_list,
 };
 
@@ -986,7 +994,7 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
        if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
                u32 temp;
                temp = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
-               adapter->ahw->capabilities2 = temp;
+               adapter->ahw->extra_capability[0] = temp;
        }
        adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
        adapter->ahw->max_mtu = nic_info.max_mtu;
@@ -1478,7 +1486,7 @@ static void qlcnic_get_lro_mss_capability(struct qlcnic_adapter *adapter)
        u32 capab = 0;
 
        if (qlcnic_82xx_check(adapter)) {
-               if (adapter->ahw->capabilities2 &
+               if (adapter->ahw->extra_capability[0] &
                    QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG)
                        adapter->flags |= QLCNIC_FW_LRO_MSS_CAP;
        } else {
@@ -1829,6 +1837,22 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter)
        return err;
 }
 
+void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u16 act_pci_fn = ahw->act_pci_func;
+       u16 count;
+
+       ahw->max_mc_count = QLCNIC_MAX_MC_COUNT;
+       if (act_pci_fn <= 2)
+               count = (QLCNIC_MAX_UC_COUNT - QLCNIC_MAX_MC_COUNT) /
+                        act_pci_fn;
+       else
+               count = (QLCNIC_LB_MAX_FILTERS - QLCNIC_MAX_MC_COUNT) /
+                        act_pci_fn;
+       ahw->max_uc_count = count;
+}
+
 int
 qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
                    int pci_using_dac)
@@ -1838,7 +1862,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
 
        adapter->rx_csum = 1;
        adapter->ahw->mc_enabled = 0;
-       adapter->ahw->max_mc_count = QLCNIC_MAX_MC_COUNT;
+       qlcnic_set_mac_filter_count(adapter);
 
        netdev->netdev_ops         = &qlcnic_netdev_ops;
        netdev->watchdog_timeo     = QLCNIC_WATCHDOG_TIMEOUTVALUE * HZ;
@@ -1876,6 +1900,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
                netdev->features |= NETIF_F_LRO;
 
        netdev->hw_features = netdev->features;
+       netdev->priv_flags |= IFF_UNICAST_FLT;
        netdev->irq = adapter->msix_entries[0].vector;
 
        err = register_netdev(netdev);
@@ -1960,6 +1985,21 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter,
        return 0;
 }
 
+void qlcnic_set_drv_version(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
+       u32 fw_cmd = 0;
+
+       if (qlcnic_82xx_check(adapter))
+               fw_cmd = QLCNIC_CMD_82XX_SET_DRV_VER;
+       else if (qlcnic_83xx_check(adapter))
+               fw_cmd = QLCNIC_CMD_83XX_SET_DRV_VER;
+
+       if ((ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) &&
+           (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_SET_DRV_VER))
+               qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd);
+}
+
 static int
 qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
@@ -1967,7 +2007,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct qlcnic_adapter *adapter = NULL;
        struct qlcnic_hardware_context *ahw;
        int err, pci_using_dac = -1;
-       u32 capab2;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
 
        if (pdev->is_virtfn)
@@ -2122,13 +2161,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto err_out_disable_mbx_intr;
 
-       if (qlcnic_82xx_check(adapter)) {
-               if (ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
-                       capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
-                       if (capab2 & QLCNIC_FW_CAPABILITY_2_OCBB)
-                               qlcnic_fw_cmd_set_drv_version(adapter);
-               }
-       }
+       qlcnic_set_drv_version(adapter);
 
        pci_set_drvdata(pdev, adapter);
 
@@ -2244,37 +2277,6 @@ static void qlcnic_remove(struct pci_dev *pdev)
        kfree(ahw);
        free_netdev(netdev);
 }
-static int __qlcnic_shutdown(struct pci_dev *pdev)
-{
-       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
-       struct net_device *netdev = adapter->netdev;
-       int retval;
-
-       netif_device_detach(netdev);
-
-       qlcnic_cancel_idc_work(adapter);
-
-       if (netif_running(netdev))
-               qlcnic_down(adapter, netdev);
-
-       qlcnic_sriov_cleanup(adapter);
-       if (qlcnic_82xx_check(adapter))
-               qlcnic_clr_all_drv_state(adapter, 0);
-
-       clear_bit(__QLCNIC_RESETTING, &adapter->state);
-
-       retval = pci_save_state(pdev);
-       if (retval)
-               return retval;
-       if (qlcnic_82xx_check(adapter)) {
-               if (qlcnic_wol_supported(adapter)) {
-                       pci_enable_wake(pdev, PCI_D3cold, 1);
-                       pci_enable_wake(pdev, PCI_D3hot, 1);
-               }
-       }
-
-       return 0;
-}
 
 static void qlcnic_shutdown(struct pci_dev *pdev)
 {
@@ -2285,8 +2287,7 @@ static void qlcnic_shutdown(struct pci_dev *pdev)
 }
 
 #ifdef CONFIG_PM
-static int
-qlcnic_suspend(struct pci_dev *pdev, pm_message_t state)
+static int qlcnic_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        int retval;
 
@@ -2298,11 +2299,9 @@ qlcnic_suspend(struct pci_dev *pdev, pm_message_t state)
        return 0;
 }
 
-static int
-qlcnic_resume(struct pci_dev *pdev)
+static int qlcnic_resume(struct pci_dev *pdev)
 {
        struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
-       struct net_device *netdev = adapter->netdev;
        int err;
 
        err = pci_enable_device(pdev);
@@ -2313,23 +2312,7 @@ qlcnic_resume(struct pci_dev *pdev)
        pci_set_master(pdev);
        pci_restore_state(pdev);
 
-       err = qlcnic_start_firmware(adapter);
-       if (err) {
-               dev_err(&pdev->dev, "failed to start firmware\n");
-               return err;
-       }
-
-       if (netif_running(netdev)) {
-               err = qlcnic_up(adapter, netdev);
-               if (err)
-                       goto done;
-
-               qlcnic_restore_indev_addr(netdev, NETDEV_UP);
-       }
-done:
-       netif_device_attach(netdev);
-       qlcnic_schedule_work(adapter, qlcnic_fw_poll_work, FW_POLL_DELAY);
-       return 0;
+       return  __qlcnic_resume(adapter);
 }
 #endif
 
@@ -2668,8 +2651,7 @@ qlcnic_clr_drv_state(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static void
-qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed)
+void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed)
 {
        u32  val;
 
@@ -3099,6 +3081,7 @@ done:
        adapter->fw_fail_cnt = 0;
        adapter->flags &= ~QLCNIC_FW_HANG;
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
+       qlcnic_set_drv_version(adapter);
 
        if (!qlcnic_clr_drv_state(adapter))
                qlcnic_schedule_work(adapter, qlcnic_fw_poll_work,
@@ -3179,8 +3162,7 @@ detach:
        return 1;
 }
 
-static void
-qlcnic_fw_poll_work(struct work_struct *work)
+void qlcnic_fw_poll_work(struct work_struct *work)
 {
        struct qlcnic_adapter *adapter = container_of(work,
                                struct qlcnic_adapter, fw_work.work);
index 4b9bab1..ab8a674 100644 (file)
@@ -15,6 +15,7 @@
 #define QLC_83XX_MINIDUMP_FLASH                0x520000
 #define QLC_83XX_OCM_INDEX                     3
 #define QLC_83XX_PCI_INDEX                     0
+#define QLC_83XX_DMA_ENGINE_INDEX              8
 
 static const u32 qlcnic_ms_read_data[] = {
        0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC
@@ -32,6 +33,16 @@ static const u32 qlcnic_ms_read_data[] = {
 
 #define QLCNIC_DUMP_MASK_MAX   0xff
 
+struct qlcnic_pex_dma_descriptor {
+       u32     read_data_size;
+       u32     dma_desc_cmd;
+       u32     src_addr_low;
+       u32     src_addr_high;
+       u32     dma_bus_addr_low;
+       u32     dma_bus_addr_high;
+       u32     rsvd[6];
+} __packed;
+
 struct qlcnic_common_entry_hdr {
        u32     type;
        u32     offset;
@@ -90,7 +101,10 @@ struct __ocm {
 } __packed;
 
 struct __mem {
-       u8      rsvd[24];
+       u32     desc_card_addr;
+       u32     dma_desc_cmd;
+       u32     start_dma_cmd;
+       u32     rsvd[3];
        u32     addr;
        u32     size;
 } __packed;
@@ -466,12 +480,12 @@ skip_poll:
        return l2->no_ops * l2->read_addr_num * sizeof(u32);
 }
 
-static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
-                             struct qlcnic_dump_entry *entry, __le32 *buffer)
+static u32 qlcnic_read_memory_test_agent(struct qlcnic_adapter *adapter,
+                                        struct __mem *mem, __le32 *buffer,
+                                        int *ret)
 {
-       u32 addr, data, test, ret = 0;
+       u32 addr, data, test;
        int i, reg_read;
-       struct __mem *mem = &entry->region.mem;
 
        reg_read = mem->size;
        addr = mem->addr;
@@ -480,7 +494,8 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
                dev_info(&adapter->pdev->dev,
                         "Unaligned memory addr:0x%x size:0x%x\n",
                         addr, reg_read);
-               return -EINVAL;
+               *ret = -EINVAL;
+               return 0;
        }
 
        mutex_lock(&adapter->ahw->mem_lock);
@@ -499,7 +514,7 @@ static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
                        if (printk_ratelimit()) {
                                dev_err(&adapter->pdev->dev,
                                        "failed to read through agent\n");
-                               ret = -EINVAL;
+                               *ret = -EIO;
                                goto out;
                        }
                }
@@ -516,6 +531,181 @@ out:
        return mem->size;
 }
 
+/* DMA register base address */
+#define QLC_DMA_REG_BASE_ADDR(dma_no)  (0x77320000 + (dma_no * 0x10000))
+
+/* DMA register offsets w.r.t base address */
+#define QLC_DMA_CMD_BUFF_ADDR_LOW      0
+#define QLC_DMA_CMD_BUFF_ADDR_HI       4
+#define QLC_DMA_CMD_STATUS_CTRL                8
+
+#define QLC_PEX_DMA_READ_SIZE          (PAGE_SIZE * 16)
+
+static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter,
+                               struct __mem *mem)
+{
+       struct qlcnic_dump_template_hdr *tmpl_hdr;
+       struct device *dev = &adapter->pdev->dev;
+       u32 dma_no, dma_base_addr, temp_addr;
+       int i, ret, dma_sts;
+
+       tmpl_hdr = adapter->ahw->fw_dump.tmpl_hdr;
+       dma_no = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       dma_base_addr = QLC_DMA_REG_BASE_ADDR(dma_no);
+
+       temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_LOW;
+       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
+                                          mem->desc_card_addr);
+       if (ret)
+               return ret;
+
+       temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_HI;
+       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, 0);
+       if (ret)
+               return ret;
+
+       temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
+       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
+                                          mem->start_dma_cmd);
+       if (ret)
+               return ret;
+
+       /* Wait for DMA to complete */
+       temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
+       for (i = 0; i < 400; i++) {
+               dma_sts = qlcnic_ind_rd(adapter, temp_addr);
+
+               if (dma_sts & BIT_1)
+                       usleep_range(250, 500);
+               else
+                       break;
+       }
+
+       if (i >= 400) {
+               dev_info(dev, "PEX DMA operation timed out");
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
+                                    struct __mem *mem,
+                                    __le32 *buffer, int *ret)
+{
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+       u32 temp, dma_base_addr, size = 0, read_size = 0;
+       struct qlcnic_pex_dma_descriptor *dma_descr;
+       struct qlcnic_dump_template_hdr *tmpl_hdr;
+       struct device *dev = &adapter->pdev->dev;
+       dma_addr_t dma_phys_addr;
+       void *dma_buffer;
+
+       tmpl_hdr = fw_dump->tmpl_hdr;
+
+       /* Check if DMA engine is available */
+       temp = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       dma_base_addr = QLC_DMA_REG_BASE_ADDR(temp);
+       temp = qlcnic_ind_rd(adapter,
+                            dma_base_addr + QLC_DMA_CMD_STATUS_CTRL);
+
+       if (!(temp & BIT_31)) {
+               dev_info(dev, "%s: DMA engine is not available\n", __func__);
+               *ret = -EIO;
+               return 0;
+       }
+
+       /* Create DMA descriptor */
+       dma_descr = kzalloc(sizeof(struct qlcnic_pex_dma_descriptor),
+                           GFP_KERNEL);
+       if (!dma_descr) {
+               *ret = -ENOMEM;
+               return 0;
+       }
+
+       /* dma_desc_cmd  0:15  = 0
+        * dma_desc_cmd 16:19  = mem->dma_desc_cmd 0:3
+        * dma_desc_cmd 20:23  = pci function number
+        * dma_desc_cmd 24:31  = mem->dma_desc_cmd 8:15
+        */
+       dma_phys_addr = fw_dump->phys_addr;
+       dma_buffer = fw_dump->dma_buffer;
+       temp = 0;
+       temp = mem->dma_desc_cmd & 0xff0f;
+       temp |= (adapter->ahw->pci_func & 0xf) << 4;
+       dma_descr->dma_desc_cmd = (temp << 16) & 0xffff0000;
+       dma_descr->dma_bus_addr_low = LSD(dma_phys_addr);
+       dma_descr->dma_bus_addr_high = MSD(dma_phys_addr);
+       dma_descr->src_addr_high = 0;
+
+       /* Collect memory dump using multiple DMA operations if required */
+       while (read_size < mem->size) {
+               if (mem->size - read_size >= QLC_PEX_DMA_READ_SIZE)
+                       size = QLC_PEX_DMA_READ_SIZE;
+               else
+                       size = mem->size - read_size;
+
+               dma_descr->src_addr_low = mem->addr + read_size;
+               dma_descr->read_data_size = size;
+
+               /* Write DMA descriptor to MS memory*/
+               temp = sizeof(struct qlcnic_pex_dma_descriptor) / 16;
+               *ret = qlcnic_83xx_ms_mem_write128(adapter, mem->desc_card_addr,
+                                                  (u32 *)dma_descr, temp);
+               if (*ret) {
+                       dev_info(dev, "Failed to write DMA descriptor to MS memory at address 0x%x\n",
+                                mem->desc_card_addr);
+                       goto free_dma_descr;
+               }
+
+               *ret = qlcnic_start_pex_dma(adapter, mem);
+               if (*ret) {
+                       dev_info(dev, "Failed to start PEX DMA operation\n");
+                       goto free_dma_descr;
+               }
+
+               memcpy(buffer, dma_buffer, size);
+               buffer += size / 4;
+               read_size += size;
+       }
+
+free_dma_descr:
+       kfree(dma_descr);
+
+       return read_size;
+}
+
+static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter,
+                             struct qlcnic_dump_entry *entry, __le32 *buffer)
+{
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+       struct device *dev = &adapter->pdev->dev;
+       struct __mem *mem = &entry->region.mem;
+       u32 data_size;
+       int ret = 0;
+
+       if (fw_dump->use_pex_dma) {
+               data_size = qlcnic_read_memory_pexdma(adapter, mem, buffer,
+                                                     &ret);
+               if (ret)
+                       dev_info(dev,
+                                "Failed to read memory dump using PEX DMA: mask[0x%x]\n",
+                                entry->hdr.mask);
+               else
+                       return data_size;
+       }
+
+       data_size = qlcnic_read_memory_test_agent(adapter, mem, buffer, &ret);
+       if (ret) {
+               dev_info(dev,
+                        "Failed to read memory dump using test agent method: mask[0x%x]\n",
+                        entry->hdr.mask);
+               return 0;
+       } else {
+               return data_size;
+       }
+}
+
 static u32 qlcnic_dump_nop(struct qlcnic_adapter *adapter,
                           struct qlcnic_dump_entry *entry, __le32 *buffer)
 {
@@ -893,6 +1083,12 @@ flash_temp:
 
        tmpl_hdr = ahw->fw_dump.tmpl_hdr;
        tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF;
+
+       if ((tmpl_hdr->version & 0xffffff) >= 0x20001)
+               ahw->fw_dump.use_pex_dma = true;
+       else
+               ahw->fw_dump.use_pex_dma = false;
+
        ahw->fw_dump.enable = 1;
 
        return 0;
@@ -910,7 +1106,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
        struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr;
        static const struct qlcnic_dump_operations *fw_dump_ops;
+       struct device *dev = &adapter->pdev->dev;
        struct qlcnic_hardware_context *ahw;
+       void *temp_buffer;
 
        ahw = adapter->ahw;
 
@@ -944,6 +1142,16 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION;
        tmpl_hdr->sys_info[1] = adapter->fw_version;
 
+       if (fw_dump->use_pex_dma) {
+               temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE,
+                                                &fw_dump->phys_addr,
+                                                GFP_KERNEL);
+               if (!temp_buffer)
+                       fw_dump->use_pex_dma = false;
+               else
+                       fw_dump->dma_buffer = temp_buffer;
+       }
+
        if (qlcnic_82xx_check(adapter)) {
                ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops);
                fw_dump_ops = qlcnic_fw_dump_ops;
@@ -1002,6 +1210,9 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
                return 0;
        }
 error:
+       if (fw_dump->use_pex_dma)
+               dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE,
+                                 fw_dump->dma_buffer, fw_dump->phys_addr);
        vfree(fw_dump->data);
        return -EINVAL;
 }
index 9176cb0..0daf660 100644 (file)
@@ -195,6 +195,8 @@ int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *,
 int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *,
                                   struct qlcnic_info *, u16);
 int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *, u16, u8);
+int qlcnic_sriov_vf_shutdown(struct pci_dev *);
+int qlcnic_sriov_vf_resume(struct qlcnic_adapter *);
 
 static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter)
 {
index bcd200e..d556249 100644 (file)
@@ -76,6 +76,8 @@ static struct qlcnic_nic_template qlcnic_sriov_vf_ops = {
        .cancel_idc_work        = qlcnic_sriov_vf_cancel_fw_work,
        .napi_add               = qlcnic_83xx_napi_add,
        .napi_del               = qlcnic_83xx_napi_del,
+       .shutdown               = qlcnic_sriov_vf_shutdown,
+       .resume                 = qlcnic_sriov_vf_resume,
        .config_ipaddr          = qlcnic_83xx_config_ipaddr,
        .clear_legacy_intr      = qlcnic_83xx_clear_legacy_intr,
 };
@@ -1954,3 +1956,54 @@ static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *adapter)
                kfree(cur);
        }
 }
+
+int qlcnic_sriov_vf_shutdown(struct pci_dev *pdev)
+{
+       struct qlcnic_adapter *adapter = pci_get_drvdata(pdev);
+       struct net_device *netdev = adapter->netdev;
+       int retval;
+
+       netif_device_detach(netdev);
+       qlcnic_cancel_idc_work(adapter);
+
+       if (netif_running(netdev))
+               qlcnic_down(adapter, netdev);
+
+       qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM);
+       qlcnic_sriov_cfg_bc_intr(adapter, 0);
+       qlcnic_83xx_disable_mbx_intr(adapter);
+       cancel_delayed_work_sync(&adapter->idc_aen_work);
+
+       retval = pci_save_state(pdev);
+       if (retval)
+               return retval;
+
+       return 0;
+}
+
+int qlcnic_sriov_vf_resume(struct qlcnic_adapter *adapter)
+{
+       struct qlc_83xx_idc *idc = &adapter->ahw->idc;
+       struct net_device *netdev = adapter->netdev;
+       int err;
+
+       set_bit(QLC_83XX_MODULE_LOADED, &idc->status);
+       qlcnic_83xx_enable_mbx_intrpt(adapter);
+       err = qlcnic_sriov_cfg_bc_intr(adapter, 1);
+       if (err)
+               return err;
+
+       err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT);
+       if (!err) {
+               if (netif_running(netdev)) {
+                       err = qlcnic_up(adapter, netdev);
+                       if (!err)
+                               qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+               }
+       }
+
+       netif_device_attach(netdev);
+       qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
+                            idc->delay);
+       return err;
+}
index 7ec030a..10ed82b 100644 (file)
@@ -168,7 +168,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter,
        if (err)
                return err;
 
-       if ((ahw->capabilities2 & QLCNIC_FW_CAPABILITY_2_BEACON)) {
+       if (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_BEACON) {
                err = qlcnic_get_beacon_state(adapter, &h_beacon_state);
                if (!err) {
                        dev_info(&adapter->pdev->dev,
index 0352345..e6acb9f 100644 (file)
@@ -1817,7 +1817,7 @@ static int cp_set_eeprom(struct net_device *dev,
 /* Put the board into D3cold state and wait for WakeUp signal */
 static void cp_set_d3_state (struct cp_private *cp)
 {
-       pci_enable_wake (cp->pdev, 0, 1); /* Enable PME# generation */
+       pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */
        pci_set_power_state (cp->pdev, PCI_D3hot);
 }
 
index 46cc11d..e7284a2 100644 (file)
@@ -21,8 +21,8 @@
 #include <linux/ethtool.h>
 #include <linux/topology.h>
 #include <linux/gfp.h>
-#include <linux/cpu_rmap.h>
 #include <linux/aer.h>
+#include <linux/interrupt.h>
 #include "net_driver.h"
 #include "efx.h"
 #include "nic.h"
@@ -1283,29 +1283,6 @@ static unsigned int efx_wanted_parallelism(struct efx_nic *efx)
        return count;
 }
 
-static int
-efx_init_rx_cpu_rmap(struct efx_nic *efx, struct msix_entry *xentries)
-{
-#ifdef CONFIG_RFS_ACCEL
-       unsigned int i;
-       int rc;
-
-       efx->net_dev->rx_cpu_rmap = alloc_irq_cpu_rmap(efx->n_rx_channels);
-       if (!efx->net_dev->rx_cpu_rmap)
-               return -ENOMEM;
-       for (i = 0; i < efx->n_rx_channels; i++) {
-               rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap,
-                                     xentries[i].vector);
-               if (rc) {
-                       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
-                       efx->net_dev->rx_cpu_rmap = NULL;
-                       return rc;
-               }
-       }
-#endif
-       return 0;
-}
-
 /* Probe the number and type of interrupts we are able to obtain, and
  * the resulting numbers of channels and RX queues.
  */
@@ -1359,11 +1336,6 @@ static int efx_probe_interrupts(struct efx_nic *efx)
                                efx->n_tx_channels = n_channels;
                                efx->n_rx_channels = n_channels;
                        }
-                       rc = efx_init_rx_cpu_rmap(efx, xentries);
-                       if (rc) {
-                               pci_disable_msix(efx->pci_dev);
-                               return rc;
-                       }
                        for (i = 0; i < efx->n_channels; i++)
                                efx_get_channel(efx, i)->irq =
                                        xentries[i].vector;
@@ -1427,6 +1399,10 @@ static void efx_start_interrupts(struct efx_nic *efx, bool may_keep_eventq)
 
        BUG_ON(efx->state == STATE_DISABLED);
 
+       if (efx->eeh_disabled_legacy_irq) {
+               enable_irq(efx->legacy_irq);
+               efx->eeh_disabled_legacy_irq = false;
+       }
        if (efx->legacy_irq)
                efx->legacy_irq_enabled = true;
        efx_nic_enable_interrupts(efx);
@@ -2365,7 +2341,7 @@ out:
  * Returns 0 if the recovery mechanisms are unsuccessful.
  * Returns a non-zero value otherwise.
  */
-static int efx_try_recovery(struct efx_nic *efx)
+int efx_try_recovery(struct efx_nic *efx)
 {
 #ifdef CONFIG_EEH
        /* A PCI error can occur and not be seen by EEH because nothing
@@ -2603,10 +2579,6 @@ static void efx_pci_remove_main(struct efx_nic *efx)
        BUG_ON(efx->state == STATE_READY);
        cancel_work_sync(&efx->reset_work);
 
-#ifdef CONFIG_RFS_ACCEL
-       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
-       efx->net_dev->rx_cpu_rmap = NULL;
-#endif
        efx_stop_interrupts(efx, false);
        efx_nic_fini_interrupt(efx);
        efx_fini_port(efx);
index 8372da2..bdb30bb 100644 (file)
@@ -124,6 +124,7 @@ extern const struct ethtool_ops efx_ethtool_ops;
 extern int efx_reset(struct efx_nic *efx, enum reset_type method);
 extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
 extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
+extern int efx_try_recovery(struct efx_nic *efx);
 
 /* Global */
 extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
index 6e76817..1fc2145 100644 (file)
@@ -1114,6 +1114,20 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
        return 0;
 }
 
+int efx_ethtool_get_ts_info(struct net_device *net_dev,
+                           struct ethtool_ts_info *ts_info)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+
+       /* Software capabilities */
+       ts_info->so_timestamping = (SOF_TIMESTAMPING_RX_SOFTWARE |
+                                   SOF_TIMESTAMPING_SOFTWARE);
+       ts_info->phc_index = -1;
+
+       efx_ptp_get_ts_info(efx, ts_info);
+       return 0;
+}
+
 static int efx_ethtool_get_module_eeprom(struct net_device *net_dev,
                                         struct ethtool_eeprom *ee,
                                         u8 *data)
@@ -1176,7 +1190,7 @@ const struct ethtool_ops efx_ethtool_ops = {
        .get_rxfh_indir_size    = efx_ethtool_get_rxfh_indir_size,
        .get_rxfh_indir         = efx_ethtool_get_rxfh_indir,
        .set_rxfh_indir         = efx_ethtool_set_rxfh_indir,
-       .get_ts_info            = efx_ptp_get_ts_info,
+       .get_ts_info            = efx_ethtool_get_ts_info,
        .get_module_info        = efx_ethtool_get_module_info,
        .get_module_eeprom      = efx_ethtool_get_module_eeprom,
 };
index 2397f0e..b74a60a 100644 (file)
@@ -1185,8 +1185,21 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb,
 
        nhoff = skb_network_offset(skb);
 
-       if (skb->protocol != htons(ETH_P_IP))
+       if (skb->protocol == htons(ETH_P_8021Q)) {
+               EFX_BUG_ON_PARANOID(skb_headlen(skb) <
+                                   nhoff + sizeof(struct vlan_hdr));
+               if (((const struct vlan_hdr *)skb->data + nhoff)->
+                   h_vlan_encapsulated_proto != htons(ETH_P_IP))
+                       return -EPROTONOSUPPORT;
+
+               /* This is IP over 802.1q VLAN.  We can't filter on the
+                * IP 5-tuple and the vlan together, so just strip the
+                * vlan header and filter on the IP part.
+                */
+               nhoff += sizeof(struct vlan_hdr);
+       } else if (skb->protocol != htons(ETH_P_IP)) {
                return -EPROTONOSUPPORT;
+       }
 
        /* RFS must validate the IP header length before calling us */
        EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip));
index 9a2914c..f4c7e6b 100644 (file)
@@ -243,6 +243,7 @@ struct efx_rx_buffer {
 #define EFX_RX_BUF_LAST_IN_PAGE        0x0001
 #define EFX_RX_PKT_CSUMMED     0x0002
 #define EFX_RX_PKT_DISCARD     0x0004
+#define EFX_RX_PKT_TCP         0x0040
 
 /**
  * struct efx_rx_page_state - Page-based rx buffer state
@@ -788,6 +789,7 @@ struct efx_nic {
        const struct efx_nic_type *type;
        int legacy_irq;
        bool legacy_irq_enabled;
+       bool eeh_disabled_legacy_irq;
        struct workqueue_struct *workqueue;
        char workqueue_name[16];
        struct work_struct reset_work;
index b0503cd..56ed3bc 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/seq_file.h>
+#include <linux/cpu_rmap.h>
 #include "net_driver.h"
 #include "bitfield.h"
 #include "efx.h"
@@ -1080,12 +1081,21 @@ efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
        rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE);
 
        if (likely(rx_ev_pkt_ok)) {
-               /* If packet is marked as OK and packet type is TCP/IP or
-                * UDP/IP, then we can rely on the hardware checksum.
+               /* If packet is marked as OK then we can rely on the
+                * hardware checksum and classification.
                 */
-               flags = (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP ||
-                        rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP) ?
-                       EFX_RX_PKT_CSUMMED : 0;
+               flags = 0;
+               switch (rx_ev_hdr_type) {
+               case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP:
+                       flags |= EFX_RX_PKT_TCP;
+                       /* fall through */
+               case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP:
+                       flags |= EFX_RX_PKT_CSUMMED;
+                       /* fall through */
+               case FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER:
+               case FSE_AZ_RX_EV_HDR_TYPE_OTHER:
+                       break;
+               }
        } else {
                flags = efx_handle_rx_not_ok(rx_queue, event);
        }
@@ -1579,6 +1589,16 @@ static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
        efx_readd(efx, &reg, FR_BZ_INT_ISR0);
        queues = EFX_EXTRACT_DWORD(reg, 0, 31);
 
+       /* Legacy interrupts are disabled too late by the EEH kernel
+        * code. Disable them earlier.
+        * If an EEH error occurred, the read will have returned all ones.
+        */
+       if (EFX_DWORD_IS_ALL_ONES(reg) && efx_try_recovery(efx) &&
+           !efx->eeh_disabled_legacy_irq) {
+               disable_irq_nosync(efx->legacy_irq);
+               efx->eeh_disabled_legacy_irq = true;
+       }
+
        /* Handle non-event-queue sources */
        if (queues & (1U << efx->irq_level)) {
                syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
@@ -1687,6 +1707,7 @@ void efx_nic_push_rx_indir_table(struct efx_nic *efx)
 int efx_nic_init_interrupt(struct efx_nic *efx)
 {
        struct efx_channel *channel;
+       unsigned int n_irqs;
        int rc;
 
        if (!EFX_INT_MODE_USE_MSI(efx)) {
@@ -1707,7 +1728,19 @@ int efx_nic_init_interrupt(struct efx_nic *efx)
                return 0;
        }
 
+#ifdef CONFIG_RFS_ACCEL
+       if (efx->interrupt_mode == EFX_INT_MODE_MSIX) {
+               efx->net_dev->rx_cpu_rmap =
+                       alloc_irq_cpu_rmap(efx->n_rx_channels);
+               if (!efx->net_dev->rx_cpu_rmap) {
+                       rc = -ENOMEM;
+                       goto fail1;
+               }
+       }
+#endif
+
        /* Hook MSI or MSI-X interrupt */
+       n_irqs = 0;
        efx_for_each_channel(channel, efx) {
                rc = request_irq(channel->irq, efx_msi_interrupt,
                                 IRQF_PROBE_SHARED, /* Not shared */
@@ -1718,13 +1751,31 @@ int efx_nic_init_interrupt(struct efx_nic *efx)
                                  "failed to hook IRQ %d\n", channel->irq);
                        goto fail2;
                }
+               ++n_irqs;
+
+#ifdef CONFIG_RFS_ACCEL
+               if (efx->interrupt_mode == EFX_INT_MODE_MSIX &&
+                   channel->channel < efx->n_rx_channels) {
+                       rc = irq_cpu_rmap_add(efx->net_dev->rx_cpu_rmap,
+                                             channel->irq);
+                       if (rc)
+                               goto fail2;
+               }
+#endif
        }
 
        return 0;
 
  fail2:
-       efx_for_each_channel(channel, efx)
+#ifdef CONFIG_RFS_ACCEL
+       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
+       efx->net_dev->rx_cpu_rmap = NULL;
+#endif
+       efx_for_each_channel(channel, efx) {
+               if (n_irqs-- == 0)
+                       break;
                free_irq(channel->irq, &efx->channel[channel->channel]);
+       }
  fail1:
        return rc;
 }
@@ -1734,11 +1785,14 @@ void efx_nic_fini_interrupt(struct efx_nic *efx)
        struct efx_channel *channel;
        efx_oword_t reg;
 
+#ifdef CONFIG_RFS_ACCEL
+       free_irq_cpu_rmap(efx->net_dev->rx_cpu_rmap);
+       efx->net_dev->rx_cpu_rmap = NULL;
+#endif
+
        /* Disable MSI/MSI-X interrupts */
-       efx_for_each_channel(channel, efx) {
-               if (channel->irq)
-                       free_irq(channel->irq, &efx->channel[channel->channel]);
-       }
+       efx_for_each_channel(channel, efx)
+               free_irq(channel->irq, &efx->channel[channel->channel]);
 
        /* ACK legacy interrupt */
        if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
index 1b00033..d63c299 100644 (file)
@@ -254,8 +254,8 @@ extern int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf,
 struct ethtool_ts_info;
 extern void efx_ptp_probe(struct efx_nic *efx);
 extern int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd);
-extern int efx_ptp_get_ts_info(struct net_device *net_dev,
-                              struct ethtool_ts_info *ts_info);
+extern void efx_ptp_get_ts_info(struct efx_nic *efx,
+                               struct ethtool_ts_info *ts_info);
 extern bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
 extern int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb);
 extern void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev);
index 9a95abf..b495394 100644 (file)
@@ -1203,18 +1203,16 @@ static int efx_ptp_ts_init(struct efx_nic *efx, struct hwtstamp_config *init)
        return 0;
 }
 
-int
-efx_ptp_get_ts_info(struct net_device *net_dev, struct ethtool_ts_info *ts_info)
+void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info)
 {
-       struct efx_nic *efx = netdev_priv(net_dev);
        struct efx_ptp_data *ptp = efx->ptp_data;
 
        if (!ptp)
-               return -EOPNOTSUPP;
+               return;
 
-       ts_info->so_timestamping = (SOF_TIMESTAMPING_TX_HARDWARE |
-                                   SOF_TIMESTAMPING_RX_HARDWARE |
-                                   SOF_TIMESTAMPING_RAW_HARDWARE);
+       ts_info->so_timestamping |= (SOF_TIMESTAMPING_TX_HARDWARE |
+                                    SOF_TIMESTAMPING_RX_HARDWARE |
+                                    SOF_TIMESTAMPING_RAW_HARDWARE);
        ts_info->phc_index = ptp_clock_index(ptp->phc_clock);
        ts_info->tx_types = 1 << HWTSTAMP_TX_OFF | 1 << HWTSTAMP_TX_ON;
        ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE |
@@ -1224,7 +1222,6 @@ efx_ptp_get_ts_info(struct net_device *net_dev, struct ethtool_ts_info *ts_info)
                               1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT |
                               1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC |
                               1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ);
-       return 0;
 }
 
 int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd)
index a7dfe36..65646cd 100644 (file)
@@ -36,7 +36,7 @@
 #define EFX_RECYCLE_RING_SIZE_NOIOMMU (2 * EFX_RX_PREFERRED_BATCH)
 
 /* Size of buffer allocated for skb header area. */
-#define EFX_SKB_HEADERS  64u
+#define EFX_SKB_HEADERS  128u
 
 /* This is the percentage fill level below which new RX descriptors
  * will be added to the RX descriptor ring.
@@ -598,6 +598,8 @@ static void efx_rx_deliver(struct efx_channel *channel, u8 *eh,
 
        /* Set the SKB flags */
        skb_checksum_none_assert(skb);
+       if (likely(rx_buf->flags & EFX_RX_PKT_CSUMMED))
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
 
        if (channel->type->receive_skb)
                if (channel->type->receive_skb(channel, skb))
@@ -627,7 +629,7 @@ void __efx_rx_packet(struct efx_channel *channel)
        if (unlikely(!(efx->net_dev->features & NETIF_F_RXCSUM)))
                rx_buf->flags &= ~EFX_RX_PKT_CSUMMED;
 
-       if (!channel->type->receive_skb)
+       if ((rx_buf->flags & EFX_RX_PKT_TCP) && !channel->type->receive_skb)
                efx_rx_packet_gro(channel, rx_buf, channel->rx_pkt_n_frags, eh);
        else
                efx_rx_deliver(channel, eh, rx_buf, channel->rx_pkt_n_frags);
@@ -675,7 +677,7 @@ static void efx_init_rx_recycle_ring(struct efx_nic *efx,
 #ifdef CONFIG_PPC64
        bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU;
 #else
-       if (efx->pci_dev->dev.iommu_group)
+       if (iommu_present(&pci_bus_type))
                bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_IOMMU;
        else
                bufs_in_recycle_ring = EFX_RECYCLE_RING_SIZE_NOIOMMU;
index 09b4f8c..0d43fa9 100644 (file)
@@ -995,7 +995,6 @@ static void bigmac_set_multicast(struct net_device *dev)
        struct bigmac *bp = netdev_priv(dev);
        void __iomem *bregs = bp->bregs;
        struct netdev_hw_addr *ha;
-       int i;
        u32 tmp, crc;
 
        /* Disable the receiver.  The bit self-clears when
index 101b037..2c3657a 100644 (file)
@@ -1700,10 +1700,10 @@ static int cpsw_probe(struct platform_device *pdev)
 
        if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
                memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
-               pr_info("Detected MACID = %pM", priv->mac_addr);
+               pr_info("Detected MACID = %pM\n", priv->mac_addr);
        } else {
                eth_random_addr(priv->mac_addr);
-               pr_info("Random MACID = %pM", priv->mac_addr);
+               pr_info("Random MACID = %pM\n", priv->mac_addr);
        }
 
        memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
index efb6f65..f118d71 100644 (file)
@@ -1532,7 +1532,7 @@ static int emac_dev_open(struct net_device *ndev)
        struct device *emac_dev = &ndev->dev;
        u32 cnt;
        struct resource *res;
-       int q, m, ret;
+       int ret;
        int i = 0;
        int k = 0;
        struct emac_priv *priv = netdev_priv(ndev);
@@ -1567,8 +1567,9 @@ static int emac_dev_open(struct net_device *ndev)
 
        while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
                for (i = res->start; i <= res->end; i++) {
-                       if (request_irq(i, emac_irq, IRQF_DISABLED,
-                                       ndev->name, ndev))
+                       if (devm_request_irq(&priv->pdev->dev, i, emac_irq,
+                                            IRQF_DISABLED,
+                                            ndev->name, ndev))
                                goto rollback;
                }
                k++;
@@ -1641,15 +1642,7 @@ static int emac_dev_open(struct net_device *ndev)
 
 rollback:
 
-       dev_err(emac_dev, "DaVinci EMAC: request_irq() failed");
-
-       for (q = k; k >= 0; k--) {
-               for (m = i; m >= res->start; m--)
-                       free_irq(m, ndev);
-               res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k-1);
-               m = res->end;
-       }
-
+       dev_err(emac_dev, "DaVinci EMAC: devm_request_irq() failed");
        ret = -EBUSY;
 err:
        pm_runtime_put(&priv->pdev->dev);
@@ -1667,9 +1660,6 @@ err:
  */
 static int emac_dev_stop(struct net_device *ndev)
 {
-       struct resource *res;
-       int i = 0;
-       int irq_num;
        struct emac_priv *priv = netdev_priv(ndev);
        struct device *emac_dev = &ndev->dev;
 
@@ -1685,13 +1675,6 @@ static int emac_dev_stop(struct net_device *ndev)
        if (priv->phydev)
                phy_disconnect(priv->phydev);
 
-       /* Free IRQ */
-       while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) {
-               for (irq_num = res->start; irq_num <= res->end; irq_num++)
-                       free_irq(irq_num, priv->ndev);
-               i++;
-       }
-
        if (netif_msg_drv(priv))
                dev_notice(emac_dev, "DaVinci EMAC: %s stopped\n", ndev->name);
 
@@ -1771,29 +1754,22 @@ static const struct net_device_ops emac_netdev_ops = {
 #endif
 };
 
-#ifdef CONFIG_OF
-static struct emac_platform_data
-       *davinci_emac_of_get_pdata(struct platform_device *pdev,
-       struct emac_priv *priv)
+static struct emac_platform_data *
+davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv)
 {
        struct device_node *np;
        struct emac_platform_data *pdata = NULL;
        const u8 *mac_addr;
-       u32 data;
-       int ret;
 
-       pdata = pdev->dev.platform_data;
-       if (!pdata) {
-               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-               if (!pdata)
-                       goto nodata;
-       }
+       if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
+               return pdev->dev.platform_data;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
 
        np = pdev->dev.of_node;
-       if (!np)
-               goto nodata;
-       else
-               pdata->version = EMAC_VERSION_2;
+       pdata->version = EMAC_VERSION_2;
 
        if (!is_valid_ether_addr(pdata->mac_addr)) {
                mac_addr = of_get_mac_address(np);
@@ -1801,47 +1777,31 @@ static struct emac_platform_data
                        memcpy(pdata->mac_addr, mac_addr, ETH_ALEN);
        }
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-reg-offset", &data);
-       if (!ret)
-               pdata->ctrl_reg_offset = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-reg-offset",
+                            &pdata->ctrl_reg_offset);
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-mod-reg-offset",
-               &data);
-       if (!ret)
-               pdata->ctrl_mod_reg_offset = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-mod-reg-offset",
+                            &pdata->ctrl_mod_reg_offset);
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-ram-offset", &data);
-       if (!ret)
-               pdata->ctrl_ram_offset = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-ram-offset",
+                            &pdata->ctrl_ram_offset);
 
-       ret = of_property_read_u32(np, "ti,davinci-ctrl-ram-size", &data);
-       if (!ret)
-               pdata->ctrl_ram_size = data;
+       of_property_read_u32(np, "ti,davinci-ctrl-ram-size",
+                            &pdata->ctrl_ram_size);
 
-       ret = of_property_read_u32(np, "ti,davinci-rmii-en", &data);
-       if (!ret)
-               pdata->rmii_en = data;
+       of_property_read_u8(np, "ti,davinci-rmii-en", &pdata->rmii_en);
 
-       ret = of_property_read_u32(np, "ti,davinci-no-bd-ram", &data);
-       if (!ret)
-               pdata->no_bd_ram = data;
+       pdata->no_bd_ram = of_property_read_bool(np, "ti,davinci-no-bd-ram");
 
        priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
        if (!priv->phy_node)
                pdata->phy_id = "";
 
        pdev->dev.platform_data = pdata;
-nodata:
+
        return  pdata;
 }
-#else
-static struct emac_platform_data
-       *davinci_emac_of_get_pdata(struct platform_device *pdev,
-       struct emac_priv *priv)
-{
-       return  pdev->dev.platform_data;
-}
-#endif
+
 /**
  * davinci_emac_probe - EMAC device probe
  * @pdev: The DaVinci EMAC device that we are removing
@@ -1856,7 +1816,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        struct resource *res;
        struct net_device *ndev;
        struct emac_priv *priv;
-       unsigned long size, hw_ram_addr;
+       unsigned long hw_ram_addr;
        struct emac_platform_data *pdata;
        struct device *emac_dev;
        struct cpdma_params dma_params;
@@ -1907,25 +1867,11 @@ static int davinci_emac_probe(struct platform_device *pdev)
        emac_dev = &ndev->dev;
        /* Get EMAC platform data */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev,"error getting res\n");
-               rc = -ENOENT;
-               goto no_pdata;
-       }
-
        priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
-       size = resource_size(res);
-       if (!devm_request_mem_region(&pdev->dev, res->start,
-                                    size, ndev->name)) {
-               dev_err(&pdev->dev, "failed request_mem_region() for regs\n");
-               rc = -ENXIO;
-               goto no_pdata;
-       }
-
-       priv->remap_addr = devm_ioremap(&pdev->dev, res->start, size);
-       if (!priv->remap_addr) {
+       priv->remap_addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->remap_addr)) {
                dev_err(&pdev->dev, "unable to map IO\n");
-               rc = -ENOMEM;
+               rc = PTR_ERR(priv->remap_addr);
                goto no_pdata;
        }
        priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset;
@@ -2076,11 +2022,13 @@ static const struct dev_pm_ops davinci_emac_pm_ops = {
        .resume         = davinci_emac_resume,
 };
 
+#if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id davinci_emac_of_match[] = {
        {.compatible = "ti,davinci-dm6467-emac", },
        {},
 };
 MODULE_DEVICE_TABLE(of, davinci_emac_of_match);
+#endif
 
 /* davinci_emac_driver: EMAC platform driver structure */
 static struct platform_driver davinci_emac_driver = {
index c47f0db..dac6f58 100644 (file)
@@ -291,6 +291,7 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_OF)
 static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
                         struct platform_device *pdev)
 {
@@ -308,7 +309,7 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
 
        return 0;
 }
-
+#endif
 
 static int davinci_mdio_probe(struct platform_device *pdev)
 {
@@ -477,11 +478,13 @@ static const struct dev_pm_ops davinci_mdio_pm_ops = {
        .resume_early   = davinci_mdio_resume,
 };
 
+#if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id davinci_mdio_of_mtable[] = {
        { .compatible = "ti,davinci_mdio", },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, davinci_mdio_of_mtable);
+#endif
 
 static struct platform_driver davinci_mdio_driver = {
        .driver = {
index 59abfbc..591437e 100644 (file)
@@ -372,7 +372,7 @@ static int tlan_resume(struct pci_dev *pdev)
 
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
-       pci_enable_wake(pdev, 0, 0);
+       pci_enable_wake(pdev, PCI_D0, 0);
        netif_device_attach(dev);
 
        if (netif_running(dev))
index 7691994..1d6dc41 100644 (file)
@@ -3211,7 +3211,7 @@ static int velocity_resume(struct device *dev)
        velocity_set_power_state(vptr, PCI_D0);
 
        if (vptr->pdev) {
-               pci_enable_wake(vptr->pdev, 0, 0);
+               pci_enable_wake(vptr->pdev, PCI_D0, 0);
                pci_restore_state(vptr->pdev);
        }
 
index d811b06..18373b6 100644 (file)
@@ -638,6 +638,14 @@ static int macvlan_ethtool_get_settings(struct net_device *dev,
        return __ethtool_get_settings(vlan->lowerdev, cmd);
 }
 
+static netdev_features_t macvlan_fix_features(struct net_device *dev,
+                                             netdev_features_t features)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       return features & (vlan->set_features | ~MACVLAN_FEATURES);
+}
+
 static const struct ethtool_ops macvlan_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_settings           = macvlan_ethtool_get_settings,
@@ -651,6 +659,7 @@ static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_stop               = macvlan_stop,
        .ndo_start_xmit         = macvlan_start_xmit,
        .ndo_change_mtu         = macvlan_change_mtu,
+       .ndo_fix_features       = macvlan_fix_features,
        .ndo_change_rx_flags    = macvlan_change_rx_flags,
        .ndo_set_mac_address    = macvlan_set_mac_address,
        .ndo_set_rx_mode        = macvlan_set_mac_lists,
@@ -791,6 +800,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
        vlan->port     = port;
        vlan->receive  = receive;
        vlan->forward  = forward;
+       vlan->set_features = MACVLAN_FEATURES;
 
        vlan->mode     = MACVLAN_MODE_VEPA;
        if (data && data[IFLA_MACVLAN_MODE])
index 5a76f20..5bfaecd 100644 (file)
@@ -65,11 +65,14 @@ static struct cdev macvtap_cdev;
 
 static const struct proto_ops macvtap_socket_ops;
 
+#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
+                     NETIF_F_TSO6 | NETIF_F_UFO)
+#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
 /*
  * RCU usage:
  * The macvtap_queue and the macvlan_dev are loosely coupled, the
  * pointers from one to the other can only be read while rcu_read_lock
- * or macvtap_lock is held.
+ * or rtnl is held.
  *
  * Both the file and the macvlan_dev hold a reference on the macvtap_queue
  * through sock_hold(&q->sk). When the macvlan_dev goes away first,
@@ -81,7 +84,6 @@ static const struct proto_ops macvtap_socket_ops;
  * file or the dev. The data structure is freed through __sk_free
  * when both our references and any pending SKBs are gone.
  */
-static DEFINE_SPINLOCK(macvtap_lock);
 
 static int macvtap_enable_queue(struct net_device *dev, struct file *file,
                                struct macvtap_queue *q)
@@ -89,7 +91,7 @@ static int macvtap_enable_queue(struct net_device *dev, struct file *file,
        struct macvlan_dev *vlan = netdev_priv(dev);
        int err = -EINVAL;
 
-       spin_lock(&macvtap_lock);
+       ASSERT_RTNL();
 
        if (q->enabled)
                goto out;
@@ -101,7 +103,6 @@ static int macvtap_enable_queue(struct net_device *dev, struct file *file,
 
        vlan->numvtaps++;
 out:
-       spin_unlock(&macvtap_lock);
        return err;
 }
 
@@ -111,7 +112,7 @@ static int macvtap_set_queue(struct net_device *dev, struct file *file,
        struct macvlan_dev *vlan = netdev_priv(dev);
        int err = -EBUSY;
 
-       spin_lock(&macvtap_lock);
+       rtnl_lock();
        if (vlan->numqueues == MAX_MACVTAP_QUEUES)
                goto out;
 
@@ -130,26 +131,25 @@ static int macvtap_set_queue(struct net_device *dev, struct file *file,
        vlan->numqueues++;
 
 out:
-       spin_unlock(&macvtap_lock);
+       rtnl_unlock();
        return err;
 }
 
-static int __macvtap_disable_queue(struct macvtap_queue *q)
+static int macvtap_disable_queue(struct macvtap_queue *q)
 {
        struct macvlan_dev *vlan;
        struct macvtap_queue *nq;
 
-       vlan = rcu_dereference_protected(q->vlan,
-                                        lockdep_is_held(&macvtap_lock));
-
+       ASSERT_RTNL();
        if (!q->enabled)
                return -EINVAL;
 
+       vlan = rtnl_dereference(q->vlan);
+
        if (vlan) {
                int index = q->queue_index;
                BUG_ON(index >= vlan->numvtaps);
-               nq = rcu_dereference_protected(vlan->taps[vlan->numvtaps - 1],
-                                              lockdep_is_held(&macvtap_lock));
+               nq = rtnl_dereference(vlan->taps[vlan->numvtaps - 1]);
                nq->queue_index = index;
 
                rcu_assign_pointer(vlan->taps[index], nq);
@@ -162,17 +162,6 @@ static int __macvtap_disable_queue(struct macvtap_queue *q)
        return 0;
 }
 
-static int macvtap_disable_queue(struct macvtap_queue *q)
-{
-       int err;
-
-       spin_lock(&macvtap_lock);
-       err = __macvtap_disable_queue(q);
-       spin_unlock(&macvtap_lock);
-
-       return err;
-}
-
 /*
  * The file owning the queue got closed, give up both
  * the reference that the files holds as well as the
@@ -185,12 +174,12 @@ static void macvtap_put_queue(struct macvtap_queue *q)
 {
        struct macvlan_dev *vlan;
 
-       spin_lock(&macvtap_lock);
-       vlan = rcu_dereference_protected(q->vlan,
-                                        lockdep_is_held(&macvtap_lock));
+       rtnl_lock();
+       vlan = rtnl_dereference(q->vlan);
+
        if (vlan) {
                if (q->enabled)
-                       BUG_ON(__macvtap_disable_queue(q));
+                       BUG_ON(macvtap_disable_queue(q));
 
                vlan->numqueues--;
                RCU_INIT_POINTER(q->vlan, NULL);
@@ -198,7 +187,7 @@ static void macvtap_put_queue(struct macvtap_queue *q)
                list_del_init(&q->next);
        }
 
-       spin_unlock(&macvtap_lock);
+       rtnl_unlock();
 
        synchronize_rcu();
        sock_put(&q->sk);
@@ -260,7 +249,7 @@ static void macvtap_del_queues(struct net_device *dev)
        struct macvtap_queue *q, *tmp, *qlist[MAX_MACVTAP_QUEUES];
        int i, j = 0;
 
-       spin_lock(&macvtap_lock);
+       ASSERT_RTNL();
        list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) {
                list_del_init(&q->next);
                qlist[j++] = q;
@@ -275,9 +264,6 @@ static void macvtap_del_queues(struct net_device *dev)
        BUG_ON(vlan->numqueues);
        /* guarantee that any future macvtap_set_queue will fail */
        vlan->numvtaps = MAX_MACVTAP_QUEUES;
-       spin_unlock(&macvtap_lock);
-
-       synchronize_rcu();
 
        for (--j; j >= 0; j--)
                sock_put(&qlist[j]->sk);
@@ -290,14 +276,44 @@ static void macvtap_del_queues(struct net_device *dev)
  */
 static int macvtap_forward(struct net_device *dev, struct sk_buff *skb)
 {
+       struct macvlan_dev *vlan = netdev_priv(dev);
        struct macvtap_queue *q = macvtap_get_queue(dev, skb);
+       netdev_features_t features;
        if (!q)
                goto drop;
 
        if (skb_queue_len(&q->sk.sk_receive_queue) >= dev->tx_queue_len)
                goto drop;
 
-       skb_queue_tail(&q->sk.sk_receive_queue, skb);
+       skb->dev = dev;
+       /* Apply the forward feature mask so that we perform segmentation
+        * according to users wishes.
+        */
+       features = netif_skb_features(skb) & vlan->tap_features;
+       if (netif_needs_gso(skb, features)) {
+               struct sk_buff *segs = __skb_gso_segment(skb, features, false);
+
+               if (IS_ERR(segs))
+                       goto drop;
+
+               if (!segs) {
+                       skb_queue_tail(&q->sk.sk_receive_queue, skb);
+                       goto wake_up;
+               }
+
+               kfree_skb(skb);
+               while (segs) {
+                       struct sk_buff *nskb = segs->next;
+
+                       segs->next = NULL;
+                       skb_queue_tail(&q->sk.sk_receive_queue, segs);
+                       segs = nskb;
+               }
+       } else {
+               skb_queue_tail(&q->sk.sk_receive_queue, skb);
+       }
+
+wake_up:
        wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND);
        return NET_RX_SUCCESS;
 
@@ -366,6 +382,11 @@ static int macvtap_newlink(struct net *src_net,
        struct macvlan_dev *vlan = netdev_priv(dev);
        INIT_LIST_HEAD(&vlan->queue_list);
 
+       /* Since macvlan supports all offloads by default, make
+        * tap support all offloads also.
+        */
+       vlan->tap_features = TUN_OFFLOADS;
+
        /* Don't put anything that may fail after macvlan_common_newlink
         * because we can't undo what it does.
         */
@@ -771,8 +792,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
 
        skb_probe_transport_header(skb, ETH_HLEN);
 
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       rcu_read_lock();
+       vlan = rcu_dereference(q->vlan);
        /* copy skb_ubuf_info for callback when skb has no error */
        if (zerocopy) {
                skb_shinfo(skb)->destructor_arg = m->msg_control;
@@ -783,7 +804,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
                macvlan_start_xmit(skb, vlan->dev);
        else
                kfree_skb(skb);
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return total_len;
 
@@ -791,11 +812,11 @@ err_kfree:
        kfree_skb(skb);
 
 err:
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       rcu_read_lock();
+       vlan = rcu_dereference(q->vlan);
        if (vlan)
                vlan->dev->stats.tx_dropped++;
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return err;
 }
@@ -871,11 +892,11 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
        copied += len;
 
 done:
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       rcu_read_lock();
+       vlan = rcu_dereference(q->vlan);
        if (vlan)
                macvlan_count_rx(vlan, copied - vnet_hdr_len, ret == 0, 0);
-       rcu_read_unlock_bh();
+       rcu_read_unlock();
 
        return ret ? ret : copied;
 }
@@ -941,11 +962,10 @@ static struct macvlan_dev *macvtap_get_vlan(struct macvtap_queue *q)
 {
        struct macvlan_dev *vlan;
 
-       rcu_read_lock_bh();
-       vlan = rcu_dereference_bh(q->vlan);
+       ASSERT_RTNL();
+       vlan = rtnl_dereference(q->vlan);
        if (vlan)
                dev_hold(vlan->dev);
-       rcu_read_unlock_bh();
 
        return vlan;
 }
@@ -976,6 +996,58 @@ static int macvtap_ioctl_set_queue(struct file *file, unsigned int flags)
        return ret;
 }
 
+static int set_offload(struct macvtap_queue *q, unsigned long arg)
+{
+       struct macvlan_dev *vlan;
+       netdev_features_t features;
+       netdev_features_t feature_mask = 0;
+
+       vlan = rtnl_dereference(q->vlan);
+       if (!vlan)
+               return -ENOLINK;
+
+       features = vlan->dev->features;
+
+       if (arg & TUN_F_CSUM) {
+               feature_mask = NETIF_F_HW_CSUM;
+
+               if (arg & (TUN_F_TSO4 | TUN_F_TSO6)) {
+                       if (arg & TUN_F_TSO_ECN)
+                               feature_mask |= NETIF_F_TSO_ECN;
+                       if (arg & TUN_F_TSO4)
+                               feature_mask |= NETIF_F_TSO;
+                       if (arg & TUN_F_TSO6)
+                               feature_mask |= NETIF_F_TSO6;
+               }
+
+               if (arg & TUN_F_UFO)
+                       feature_mask |= NETIF_F_UFO;
+       }
+
+       /* tun/tap driver inverts the usage for TSO offloads, where
+        * setting the TSO bit means that the userspace wants to
+        * accept TSO frames and turning it off means that user space
+        * does not support TSO.
+        * For macvtap, we have to invert it to mean the same thing.
+        * When user space turns off TSO, we turn off GSO/LRO so that
+        * user-space will not receive TSO frames.
+        */
+       if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO))
+               features |= RX_OFFLOADS;
+       else
+               features &= ~RX_OFFLOADS;
+
+       /* tap_features are the same as features on tun/tap and
+        * reflect user expectations.
+        */
+       vlan->tap_features = vlan->dev->features &
+                           (feature_mask | ~TUN_OFFLOADS);
+       vlan->set_features = features;
+       netdev_update_features(vlan->dev);
+
+       return 0;
+}
+
 /*
  * provide compatibility with generic tun/tap interface
  */
@@ -1008,21 +1080,27 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                return ret;
 
        case TUNGETIFF:
+               rtnl_lock();
                vlan = macvtap_get_vlan(q);
-               if (!vlan)
+               if (!vlan) {
+                       rtnl_unlock();
                        return -ENOLINK;
+               }
 
                ret = 0;
                if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) ||
                    put_user(q->flags, &ifr->ifr_flags))
                        ret = -EFAULT;
                macvtap_put_vlan(vlan);
+               rtnl_unlock();
                return ret;
 
        case TUNSETQUEUE:
                if (get_user(u, &ifr->ifr_flags))
                        return -EFAULT;
-               return macvtap_ioctl_set_queue(file, u);
+               rtnl_lock();
+               ret = macvtap_ioctl_set_queue(file, u);
+               rtnl_unlock();
 
        case TUNGETFEATURES:
                if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR |
@@ -1062,7 +1140,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                         got enabled for forwarded frames */
                if (!(q->flags & IFF_VNET_HDR))
                        return  -EINVAL;
-               return 0;
+               rtnl_lock();
+               ret = set_offload(q, arg);
+               rtnl_unlock();
+               return ret;
 
        default:
                return -EINVAL;
diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c
new file mode 100644 (file)
index 0000000..a0baf56
--- /dev/null
@@ -0,0 +1,174 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/net_namespace.h>
+#include <linux/if_arp.h>
+
+struct pcpu_lstats {
+       u64 packets;
+       u64 bytes;
+       struct u64_stats_sync syncp;
+};
+
+static netdev_tx_t nlmon_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       int len = skb->len;
+       struct pcpu_lstats *stats = this_cpu_ptr(dev->lstats);
+
+       u64_stats_update_begin(&stats->syncp);
+       stats->bytes += len;
+       stats->packets++;
+       u64_stats_update_end(&stats->syncp);
+
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+static int nlmon_is_valid_mtu(int new_mtu)
+{
+       /* Note that in netlink we do not really have an upper limit. On
+        * default, we use NLMSG_GOODSIZE. Here at least we should make
+        * sure that it's at least the header size.
+        */
+       return new_mtu >= (int) sizeof(struct nlmsghdr);
+}
+
+static int nlmon_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (!nlmon_is_valid_mtu(new_mtu))
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+static int nlmon_dev_init(struct net_device *dev)
+{
+       dev->lstats = alloc_percpu(struct pcpu_lstats);
+
+       return dev->lstats == NULL ? -ENOMEM : 0;
+}
+
+static void nlmon_dev_uninit(struct net_device *dev)
+{
+       free_percpu(dev->lstats);
+}
+
+static struct netlink_tap nlmon_tap;
+
+static int nlmon_open(struct net_device *dev)
+{
+       return netlink_add_tap(&nlmon_tap);
+}
+
+static int nlmon_close(struct net_device *dev)
+{
+       return netlink_remove_tap(&nlmon_tap);
+}
+
+static struct rtnl_link_stats64 *
+nlmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+       int i;
+       u64 bytes = 0, packets = 0;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_lstats *nl_stats;
+               u64 tbytes, tpackets;
+               unsigned int start;
+
+               nl_stats = per_cpu_ptr(dev->lstats, i);
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&nl_stats->syncp);
+                       tbytes = nl_stats->bytes;
+                       tpackets = nl_stats->packets;
+               } while (u64_stats_fetch_retry_bh(&nl_stats->syncp, start));
+
+               packets += tpackets;
+               bytes += tbytes;
+       }
+
+       stats->rx_packets = packets;
+       stats->tx_packets = 0;
+
+       stats->rx_bytes = bytes;
+       stats->tx_bytes = 0;
+
+       return stats;
+}
+
+static u32 always_on(struct net_device *dev)
+{
+       return 1;
+}
+
+static const struct ethtool_ops nlmon_ethtool_ops = {
+       .get_link = always_on,
+};
+
+static const struct net_device_ops nlmon_ops = {
+       .ndo_init = nlmon_dev_init,
+       .ndo_uninit = nlmon_dev_uninit,
+       .ndo_open = nlmon_open,
+       .ndo_stop = nlmon_close,
+       .ndo_start_xmit = nlmon_xmit,
+       .ndo_get_stats64 = nlmon_get_stats64,
+       .ndo_change_mtu = nlmon_change_mtu,
+};
+
+static struct netlink_tap nlmon_tap __read_mostly = {
+       .module = THIS_MODULE,
+};
+
+static void nlmon_setup(struct net_device *dev)
+{
+       dev->type = ARPHRD_NETLINK;
+       dev->tx_queue_len = 0;
+
+       dev->netdev_ops = &nlmon_ops;
+       dev->ethtool_ops = &nlmon_ethtool_ops;
+       dev->destructor = free_netdev;
+
+       dev->features = NETIF_F_FRAGLIST | NETIF_F_HIGHDMA;
+       dev->flags = IFF_NOARP;
+
+       /* That's rather a softlimit here, which, of course,
+        * can be altered. Not a real MTU, but what is to be
+        * expected in most cases.
+        */
+       dev->mtu = NLMSG_GOODSIZE;
+}
+
+static __init int nlmon_register(void)
+{
+       int err;
+       struct net_device *nldev;
+
+       nldev = nlmon_tap.dev = alloc_netdev(0, "netlink", nlmon_setup);
+       if (unlikely(nldev == NULL))
+               return -ENOMEM;
+
+       err = register_netdev(nldev);
+       if (unlikely(err))
+               free_netdev(nldev);
+
+       return err;
+}
+
+static __exit void nlmon_unregister(void)
+{
+       struct net_device *nldev = nlmon_tap.dev;
+
+       unregister_netdev(nldev);
+}
+
+module_init(nlmon_register);
+module_exit(nlmon_unregister);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
+MODULE_AUTHOR("Mathieu Geli <geli@enseirb.fr>");
+MODULE_DESCRIPTION("Netlink monitoring device");
index bd8758f..1e3c302 100644 (file)
@@ -1371,7 +1371,7 @@ static int ax88179_stop(struct usbnet *dev)
 }
 
 static const struct driver_info ax88179_info = {
-       .description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+       .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet",
        .bind = ax88179_bind,
        .unbind = ax88179_unbind,
        .status = ax88179_status,
@@ -1384,7 +1384,7 @@ static const struct driver_info ax88179_info = {
 };
 
 static const struct driver_info ax88178a_info = {
-       .description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+       .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet",
        .bind = ax88179_bind,
        .unbind = ax88179_unbind,
        .status = ax88179_status,
@@ -1433,6 +1433,7 @@ static struct usb_driver ax88179_178a_driver = {
        .probe =        usbnet_probe,
        .suspend =      ax88179_suspend,
        .resume =       ax88179_resume,
+       .reset_resume = ax88179_resume,
        .disconnect =   usbnet_disconnect,
        .supports_autosuspend = 1,
        .disable_hub_initiated_lpm = 1,
index 2c02b4e..1abf1d4 100644 (file)
@@ -31,5 +31,6 @@ source "drivers/net/wireless/ath/carl9170/Kconfig"
 source "drivers/net/wireless/ath/ath6kl/Kconfig"
 source "drivers/net/wireless/ath/ar5523/Kconfig"
 source "drivers/net/wireless/ath/wil6210/Kconfig"
+source "drivers/net/wireless/ath/ath10k/Kconfig"
 
 endif
index 97b964d..fb05cfd 100644 (file)
@@ -4,6 +4,7 @@ obj-$(CONFIG_CARL9170)          += carl9170/
 obj-$(CONFIG_ATH6KL)           += ath6kl/
 obj-$(CONFIG_AR5523)           += ar5523/
 obj-$(CONFIG_WIL6210)          += wil6210/
+obj-$(CONFIG_ATH10K)           += ath10k/
 
 obj-$(CONFIG_ATH_COMMON)       += ath.o
 
index 4521342..daeafef 100644 (file)
@@ -239,13 +239,12 @@ enum ATH_DEBUG {
        ATH_DBG_CONFIG          = 0x00000200,
        ATH_DBG_FATAL           = 0x00000400,
        ATH_DBG_PS              = 0x00000800,
-       ATH_DBG_HWTIMER         = 0x00001000,
-       ATH_DBG_BTCOEX          = 0x00002000,
-       ATH_DBG_WMI             = 0x00004000,
-       ATH_DBG_BSTUCK          = 0x00008000,
-       ATH_DBG_MCI             = 0x00010000,
-       ATH_DBG_DFS             = 0x00020000,
-       ATH_DBG_WOW             = 0x00040000,
+       ATH_DBG_BTCOEX          = 0x00001000,
+       ATH_DBG_WMI             = 0x00002000,
+       ATH_DBG_BSTUCK          = 0x00004000,
+       ATH_DBG_MCI             = 0x00008000,
+       ATH_DBG_DFS             = 0x00010000,
+       ATH_DBG_WOW             = 0x00020000,
        ATH_DBG_ANY             = 0xffffffff
 };
 
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
new file mode 100644 (file)
index 0000000..cde58fe
--- /dev/null
@@ -0,0 +1,39 @@
+config ATH10K
+        tristate "Atheros 802.11ac wireless cards support"
+        depends on MAC80211
+       select ATH_COMMON
+        ---help---
+          This module adds support for wireless adapters based on
+          Atheros IEEE 802.11ac family of chipsets.
+
+          If you choose to build a module, it'll be called ath10k.
+
+config ATH10K_PCI
+       tristate "Atheros ath10k PCI support"
+       depends on ATH10K && PCI
+       ---help---
+         This module adds support for PCIE bus
+
+config ATH10K_DEBUG
+       bool "Atheros ath10k debugging"
+       depends on ATH10K
+       ---help---
+         Enables debug support
+
+         If unsure, say Y to make it easier to debug problems.
+
+config ATH10K_DEBUGFS
+       bool "Atheros ath10k debugfs support"
+       depends on ATH10K
+       ---help---
+         Enabled debugfs support
+
+         If unsure, say Y to make it easier to debug problems.
+
+config ATH10K_TRACING
+       bool "Atheros ath10k tracing support"
+       depends on ATH10K
+       depends on EVENT_TRACING
+       ---help---
+         Select this to ath10k use tracing infrastructure.
+
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
new file mode 100644 (file)
index 0000000..a4179f4
--- /dev/null
@@ -0,0 +1,20 @@
+obj-$(CONFIG_ATH10K) += ath10k_core.o
+ath10k_core-y += mac.o \
+                debug.o \
+                core.o \
+                htc.o \
+                htt.o \
+                htt_rx.o \
+                htt_tx.o \
+                txrx.o \
+                wmi.o \
+                bmi.o
+
+ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
+
+obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
+ath10k_pci-y += pci.o \
+               ce.o
+
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
new file mode 100644 (file)
index 0000000..1a2ef51
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 "bmi.h"
+#include "hif.h"
+#include "debug.h"
+#include "htc.h"
+
+int ath10k_bmi_done(struct ath10k *ar)
+{
+       struct bmi_cmd cmd;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
+               return 0;
+       }
+
+       ar->bmi.done_sent = true;
+       cmd.id = __cpu_to_le32(BMI_DONE);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+       if (ret) {
+               ath10k_warn("unable to write to the device: %d\n", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
+       return 0;
+}
+
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+                              struct bmi_target_info *target_info)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
+       u32 resplen = sizeof(resp.get_target_info);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("BMI Get Target Info Command disallowed\n");
+               return -EBUSY;
+       }
+
+       cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+       if (ret) {
+               ath10k_warn("unable to get target info from device\n");
+               return ret;
+       }
+
+       if (resplen < sizeof(resp.get_target_info)) {
+               ath10k_warn("invalid get_target_info response length (%d)\n",
+                           resplen);
+               return -EIO;
+       }
+
+       target_info->version = __le32_to_cpu(resp.get_target_info.version);
+       target_info->type    = __le32_to_cpu(resp.get_target_info.type);
+       return 0;
+}
+
+int ath10k_bmi_read_memory(struct ath10k *ar,
+                          u32 address, void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
+       u32 rxlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+                  __func__, ar, address, length);
+
+       while (length) {
+               rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
+
+               cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
+               cmd.read_mem.addr = __cpu_to_le32(address);
+               cmd.read_mem.len  = __cpu_to_le32(rxlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
+                                                 &resp, &rxlen);
+               if (ret) {
+                       ath10k_warn("unable to read from the device\n");
+                       return ret;
+               }
+
+               memcpy(buffer, resp.read_mem.payload, rxlen);
+               address += rxlen;
+               buffer  += rxlen;
+               length  -= rxlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_write_memory(struct ath10k *ar,
+                           u32 address, const void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
+       u32 txlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+                  __func__, ar, address, length);
+
+       while (length) {
+               txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+               /* copy before roundup to avoid reading beyond buffer*/
+               memcpy(cmd.write_mem.payload, buffer, txlen);
+               txlen = roundup(txlen, 4);
+
+               cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
+               cmd.write_mem.addr = __cpu_to_le32(address);
+               cmd.write_mem.len  = __cpu_to_le32(txlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+                                                 NULL, NULL);
+               if (ret) {
+                       ath10k_warn("unable to write to the device\n");
+                       return ret;
+               }
+
+               /* fixup roundup() so `length` zeroes out for last chunk */
+               txlen = min(txlen, length);
+
+               address += txlen;
+               buffer  += txlen;
+               length  -= txlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
+{
+       struct bmi_cmd cmd;
+       union bmi_resp resp;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
+       u32 resplen = sizeof(resp.execute);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
+                  __func__, ar, address, *param);
+
+       cmd.id            = __cpu_to_le32(BMI_EXECUTE);
+       cmd.execute.addr  = __cpu_to_le32(address);
+       cmd.execute.param = __cpu_to_le32(*param);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+       if (ret) {
+               ath10k_warn("unable to read from the device\n");
+               return ret;
+       }
+
+       if (resplen < sizeof(resp.execute)) {
+               ath10k_warn("invalid execute response length (%d)\n",
+                           resplen);
+               return ret;
+       }
+
+       *param = __le32_to_cpu(resp.execute.result);
+       return 0;
+}
+
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
+{
+       struct bmi_cmd cmd;
+       u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
+       u32 txlen;
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       while (length) {
+               txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+               WARN_ON_ONCE(txlen & 3);
+
+               cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
+               cmd.lz_data.len = __cpu_to_le32(txlen);
+               memcpy(cmd.lz_data.payload, buffer, txlen);
+
+               ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+                                                 NULL, NULL);
+               if (ret) {
+                       ath10k_warn("unable to write to the device\n");
+                       return ret;
+               }
+
+               buffer += txlen;
+               length -= txlen;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
+{
+       struct bmi_cmd cmd;
+       u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
+       int ret;
+
+       if (ar->bmi.done_sent) {
+               ath10k_warn("command disallowed\n");
+               return -EBUSY;
+       }
+
+       cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
+       cmd.lz_start.addr = __cpu_to_le32(address);
+
+       ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+       if (ret) {
+               ath10k_warn("unable to Start LZ Stream to the device\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_bmi_fast_download(struct ath10k *ar,
+                            u32 address, const void *buffer, u32 length)
+{
+       u8 trailer[4] = {};
+       u32 head_len = rounddown(length, 4);
+       u32 trailer_len = length - head_len;
+       int ret;
+
+       ret = ath10k_bmi_lz_stream_start(ar, address);
+       if (ret)
+               return ret;
+
+       /* copy the last word into a zero padded buffer */
+       if (trailer_len > 0)
+               memcpy(trailer, buffer + head_len, trailer_len);
+
+       ret = ath10k_bmi_lz_data(ar, buffer, head_len);
+       if (ret)
+               return ret;
+
+       if (trailer_len > 0)
+               ret = ath10k_bmi_lz_data(ar, trailer, 4);
+
+       if (ret != 0)
+               return ret;
+
+       /*
+        * Close compressed stream and open a new (fake) one.
+        * This serves mainly to flush Target caches.
+        */
+       ret = ath10k_bmi_lz_stream_start(ar, 0x00);
+
+       return ret;
+}
diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
new file mode 100644 (file)
index 0000000..32c56aa
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _BMI_H_
+#define _BMI_H_
+
+#include "core.h"
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to QCA988x, to provide
+ * patches to code that is already resident on QCA988x, and generally
+ * to examine and modify state.  The Host has an opportunity to use
+ * BMI only once during bootup.  Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0.   BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using AR8K Counter #4.  As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * BMI handles all required Target-side cache flushing.
+ */
+
+/* Maximum data size used for BMI transfers */
+#define BMI_MAX_DATA_SIZE      256
+
+/* len = cmd + addr + length */
+#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \
+                       sizeof(u32) + \
+                       sizeof(u32) + \
+                       sizeof(u32))
+
+/* BMI Commands */
+
+enum bmi_cmd_id {
+       BMI_NO_COMMAND          = 0,
+       BMI_DONE                = 1,
+       BMI_READ_MEMORY         = 2,
+       BMI_WRITE_MEMORY        = 3,
+       BMI_EXECUTE             = 4,
+       BMI_SET_APP_START       = 5,
+       BMI_READ_SOC_REGISTER   = 6,
+       BMI_READ_SOC_WORD       = 6,
+       BMI_WRITE_SOC_REGISTER  = 7,
+       BMI_WRITE_SOC_WORD      = 7,
+       BMI_GET_TARGET_ID       = 8,
+       BMI_GET_TARGET_INFO     = 8,
+       BMI_ROMPATCH_INSTALL    = 9,
+       BMI_ROMPATCH_UNINSTALL  = 10,
+       BMI_ROMPATCH_ACTIVATE   = 11,
+       BMI_ROMPATCH_DEACTIVATE = 12,
+       BMI_LZ_STREAM_START     = 13, /* should be followed by LZ_DATA */
+       BMI_LZ_DATA             = 14,
+       BMI_NVRAM_PROCESS       = 15,
+};
+
+#define BMI_NVRAM_SEG_NAME_SZ 16
+
+struct bmi_cmd {
+       __le32 id; /* enum bmi_cmd_id */
+       union {
+               struct {
+               } done;
+               struct {
+                       __le32 addr;
+                       __le32 len;
+               } read_mem;
+               struct {
+                       __le32 addr;
+                       __le32 len;
+                       u8 payload[0];
+               } write_mem;
+               struct {
+                       __le32 addr;
+                       __le32 param;
+               } execute;
+               struct {
+                       __le32 addr;
+               } set_app_start;
+               struct {
+                       __le32 addr;
+               } read_soc_reg;
+               struct {
+                       __le32 addr;
+                       __le32 value;
+               } write_soc_reg;
+               struct {
+               } get_target_info;
+               struct {
+                       __le32 rom_addr;
+                       __le32 ram_addr; /* or value */
+                       __le32 size;
+                       __le32 activate; /* 0=install, but dont activate */
+               } rompatch_install;
+               struct {
+                       __le32 patch_id;
+               } rompatch_uninstall;
+               struct {
+                       __le32 count;
+                       __le32 patch_ids[0]; /* length of @count */
+               } rompatch_activate;
+               struct {
+                       __le32 count;
+                       __le32 patch_ids[0]; /* length of @count */
+               } rompatch_deactivate;
+               struct {
+                       __le32 addr;
+               } lz_start;
+               struct {
+                       __le32 len; /* max BMI_MAX_DATA_SIZE */
+                       u8 payload[0]; /* length of @len */
+               } lz_data;
+               struct {
+                       u8 name[BMI_NVRAM_SEG_NAME_SZ];
+               } nvram_process;
+               u8 payload[BMI_MAX_CMDBUF_SIZE];
+       };
+} __packed;
+
+union bmi_resp {
+       struct {
+               u8 payload[0];
+       } read_mem;
+       struct {
+               __le32 result;
+       } execute;
+       struct {
+               __le32 value;
+       } read_soc_reg;
+       struct {
+               __le32 len;
+               __le32 version;
+               __le32 type;
+       } get_target_info;
+       struct {
+               __le32 patch_id;
+       } rompatch_install;
+       struct {
+               __le32 patch_id;
+       } rompatch_uninstall;
+       struct {
+               /* 0 = nothing executed
+                * otherwise = NVRAM segment return value */
+               __le32 result;
+       } nvram_process;
+       u8 payload[BMI_MAX_CMDBUF_SIZE];
+} __packed;
+
+struct bmi_target_info {
+       u32 version;
+       u32 type;
+};
+
+
+/* in msec */
+#define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ)
+
+#define BMI_CE_NUM_TO_TARG 0
+#define BMI_CE_NUM_TO_HOST 1
+
+int ath10k_bmi_done(struct ath10k *ar);
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+                              struct bmi_target_info *target_info);
+int ath10k_bmi_read_memory(struct ath10k *ar, u32 address,
+                          void *buffer, u32 length);
+int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
+                           const void *buffer, u32 length);
+
+#define ath10k_bmi_read32(ar, item, val)                               \
+       ({                                                              \
+               int ret;                                                \
+               u32 addr;                                               \
+               __le32 tmp;                                             \
+                                                                       \
+               addr = host_interest_item_address(HI_ITEM(item));       \
+               ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
+               *val = __le32_to_cpu(tmp);                              \
+               ret;                                                    \
+        })
+
+#define ath10k_bmi_write32(ar, item, val)                              \
+       ({                                                              \
+               int ret;                                                \
+               u32 address;                                            \
+               __le32 v = __cpu_to_le32(val);                          \
+                                                                       \
+               address = host_interest_item_address(HI_ITEM(item));    \
+               ret = ath10k_bmi_write_memory(ar, address,              \
+                                             (u8 *)&v, sizeof(v));     \
+               ret;                                                    \
+       })
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param);
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address);
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length);
+int ath10k_bmi_fast_download(struct ath10k *ar, u32 address,
+                            const void *buffer, u32 length);
+#endif /* _BMI_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
new file mode 100644 (file)
index 0000000..61a8ac7
--- /dev/null
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 "hif.h"
+#include "pci.h"
+#include "ce.h"
+#include "debug.h"
+
+/*
+ * Support for Copy Engine hardware, which is mainly used for
+ * communication between Host and Target over a PCIe interconnect.
+ */
+
+/*
+ * A single CopyEngine (CE) comprises two "rings":
+ *   a source ring
+ *   a destination ring
+ *
+ * Each ring consists of a number of descriptors which specify
+ * an address, length, and meta-data.
+ *
+ * Typically, one side of the PCIe interconnect (Host or Target)
+ * controls one ring and the other side controls the other ring.
+ * The source side chooses when to initiate a transfer and it
+ * chooses what to send (buffer address, length). The destination
+ * side keeps a supply of "anonymous receive buffers" available and
+ * it handles incoming data as it arrives (when the destination
+ * recieves an interrupt).
+ *
+ * The sender may send a simple buffer (address/length) or it may
+ * send a small list of buffers.  When a small list is sent, hardware
+ * "gathers" these and they end up in a single destination buffer
+ * with a single interrupt.
+ *
+ * There are several "contexts" managed by this layer -- more, it
+ * may seem -- than should be needed. These are provided mainly for
+ * maximum flexibility and especially to facilitate a simpler HIF
+ * implementation. There are per-CopyEngine recv, send, and watermark
+ * contexts. These are supplied by the caller when a recv, send,
+ * or watermark handler is established and they are echoed back to
+ * the caller when the respective callbacks are invoked. There is
+ * also a per-transfer context supplied by the caller when a buffer
+ * (or sendlist) is sent and when a buffer is enqueued for recv.
+ * These per-transfer contexts are echoed back to the caller when
+ * the buffer is sent/received.
+ */
+
+static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar,
+                                                      u32 ce_ctrl_addr,
+                                                      unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n);
+}
+
+static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar,
+                                                     u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
+                                                     u32 ce_ctrl_addr,
+                                                     unsigned int n)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *indicator_addr;
+
+       if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+               return;
+       }
+
+       /* workaround for QCA988x_1.0 HW CE */
+       indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS;
+
+       if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) {
+               iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr);
+       } else {
+               unsigned long irq_flags;
+               local_irq_save(irq_flags);
+               iowrite32(1, indicator_addr);
+
+               /*
+                * PCIE write waits for ACK in IPQ8K, there is no
+                * need to read back value.
+                */
+               (void)ioread32(indicator_addr);
+               (void)ioread32(indicator_addr); /* conservative */
+
+               ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+
+               iowrite32(0, indicator_addr);
+               local_irq_restore(irq_flags);
+       }
+}
+
+static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS);
+}
+
+static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int addr)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar,
+                                              u32 ce_ctrl_addr,
+                                              unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
+                                              u32 ce_ctrl_addr,
+                                              unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32((ar),
+                                          (ce_ctrl_addr) + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr &  ~CE_CTRL1_DMAX_LENGTH_MASK) |
+                          CE_CTRL1_DMAX_LENGTH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) |
+                          CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    unsigned int n)
+{
+       u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+                          (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) |
+                          CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr)
+{
+       return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    u32 addr)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar,
+                                               u32 ce_ctrl_addr,
+                                               unsigned int n)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
+                                                  u32 ce_ctrl_addr,
+                                                  unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+                          (addr & ~SRC_WATERMARK_HIGH_MASK) |
+                          SRC_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
+                                                 u32 ce_ctrl_addr,
+                                                 unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+                          (addr & ~SRC_WATERMARK_LOW_MASK) |
+                          SRC_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr,
+                                                   unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+                          (addr & ~DST_WATERMARK_HIGH_MASK) |
+                          DST_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
+                                                  u32 ce_ctrl_addr,
+                                                  unsigned int n)
+{
+       u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+                          (addr & ~DST_WATERMARK_LOW_MASK) |
+                          DST_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
+                                                       u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr | HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
+                                                       u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
+                                                   u32 ce_ctrl_addr)
+{
+       u32 host_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + HOST_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+                          host_ie_addr & ~CE_WATERMARK_MASK);
+}
+
+static inline void ath10k_ce_error_intr_enable(struct ath10k *ar,
+                                              u32 ce_ctrl_addr)
+{
+       u32 misc_ie_addr = ath10k_pci_read32(ar,
+                                            ce_ctrl_addr + MISC_IE_ADDRESS);
+
+       ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
+                          misc_ie_addr | CE_ERROR_MASK);
+}
+
+static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
+                                                    u32 ce_ctrl_addr,
+                                                    unsigned int mask)
+{
+       ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+}
+
+
+/*
+ * Guts of ath10k_ce_send, used by both ath10k_ce_send and
+ * ath10k_ce_sendlist_send.
+ * The caller takes responsibility for any needed locking.
+ */
+static int ath10k_ce_send_nolock(struct ce_state *ce_state,
+                                void *per_transfer_context,
+                                u32 buffer,
+                                unsigned int nbytes,
+                                unsigned int transfer_id,
+                                unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ce_desc *desc, *sdesc;
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int sw_index = src_ring->sw_index;
+       unsigned int write_index = src_ring->write_index;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       u32 desc_flags = 0;
+       int ret = 0;
+
+       if (nbytes > ce_state->src_sz_max)
+               ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
+                           __func__, nbytes, ce_state->src_sz_max);
+
+       ath10k_pci_wake(ar);
+
+       if (unlikely(CE_RING_DELTA(nentries_mask,
+                                  write_index, sw_index - 1) <= 0)) {
+               ret = -EIO;
+               goto exit;
+       }
+
+       desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space,
+                                  write_index);
+       sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index);
+
+       desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA);
+
+       if (flags & CE_SEND_FLAG_GATHER)
+               desc_flags |= CE_DESC_FLAGS_GATHER;
+       if (flags & CE_SEND_FLAG_BYTE_SWAP)
+               desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
+
+       sdesc->addr   = __cpu_to_le32(buffer);
+       sdesc->nbytes = __cpu_to_le16(nbytes);
+       sdesc->flags  = __cpu_to_le16(desc_flags);
+
+       *desc = *sdesc;
+
+       src_ring->per_transfer_context[write_index] = per_transfer_context;
+
+       /* Update Source Ring Write Index */
+       write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+
+       /* WORKAROUND */
+       if (!(flags & CE_SEND_FLAG_GATHER))
+               ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
+
+       src_ring->write_index = write_index;
+exit:
+       ath10k_pci_sleep(ar);
+       return ret;
+}
+
+int ath10k_ce_send(struct ce_state *ce_state,
+                  void *per_transfer_context,
+                  u32 buffer,
+                  unsigned int nbytes,
+                  unsigned int transfer_id,
+                  unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+                                   buffer, nbytes, transfer_id, flags);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
+                               unsigned int nbytes, u32 flags)
+{
+       unsigned int num_items = sendlist->num_items;
+       struct ce_sendlist_item *item;
+
+       item = &sendlist->item[num_items];
+       item->data = buffer;
+       item->u.nbytes = nbytes;
+       item->flags = flags;
+       sendlist->num_items++;
+}
+
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+                           void *per_transfer_context,
+                           struct ce_sendlist *sendlist,
+                           unsigned int transfer_id)
+{
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       struct ce_sendlist_item *item;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int num_items = sendlist->num_items;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int i, delta, ret = -ENOMEM;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
+       delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
+
+       if (delta >= num_items) {
+               /*
+                * Handle all but the last item uniformly.
+                */
+               for (i = 0; i < num_items - 1; i++) {
+                       item = &sendlist->item[i];
+                       ret = ath10k_ce_send_nolock(ce_state,
+                                                   CE_SENDLIST_ITEM_CTXT,
+                                                   (u32) item->data,
+                                                   item->u.nbytes, transfer_id,
+                                                   item->flags |
+                                                   CE_SEND_FLAG_GATHER);
+                       if (ret)
+                               ath10k_warn("CE send failed for item: %d\n", i);
+               }
+               /*
+                * Provide valid context pointer for final item.
+                */
+               item = &sendlist->item[i];
+               ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+                                           (u32) item->data, item->u.nbytes,
+                                           transfer_id, item->flags);
+               if (ret)
+                       ath10k_warn("CE send failed for last item: %d\n", i);
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+                              void *per_recv_context,
+                              u32 buffer)
+{
+       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       unsigned int nentries_mask = dest_ring->nentries_mask;
+       unsigned int write_index;
+       unsigned int sw_index;
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       write_index = dest_ring->write_index;
+       sw_index = dest_ring->sw_index;
+
+       ath10k_pci_wake(ar);
+
+       if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
+               struct ce_desc *base = dest_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
+
+               /* Update destination descriptor */
+               desc->addr    = __cpu_to_le32(buffer);
+               desc->nbytes = 0;
+
+               dest_ring->per_transfer_context[write_index] =
+                                                       per_recv_context;
+
+               /* Update Destination Ring Write Index */
+               write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+               ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
+               dest_ring->write_index = write_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+       ath10k_pci_sleep(ar);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_recv_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
+                                               void **per_transfer_contextp,
+                                               u32 *bufferp,
+                                               unsigned int *nbytesp,
+                                               unsigned int *transfer_idp,
+                                               unsigned int *flagsp)
+{
+       struct ce_ring_state *dest_ring = ce_state->dest_ring;
+       unsigned int nentries_mask = dest_ring->nentries_mask;
+       unsigned int sw_index = dest_ring->sw_index;
+
+       struct ce_desc *base = dest_ring->base_addr_owner_space;
+       struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+       struct ce_desc sdesc;
+       u16 nbytes;
+
+       /* Copy in one go for performance reasons */
+       sdesc = *desc;
+
+       nbytes = __le16_to_cpu(sdesc.nbytes);
+       if (nbytes == 0) {
+               /*
+                * This closes a relatively unusual race where the Host
+                * sees the updated DRRI before the update to the
+                * corresponding descriptor has completed. We treat this
+                * as a descriptor that is not yet done.
+                */
+               return -EIO;
+       }
+
+       desc->nbytes = 0;
+
+       /* Return data from completed destination descriptor */
+       *bufferp = __le32_to_cpu(sdesc.addr);
+       *nbytesp = nbytes;
+       *transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA);
+
+       if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP)
+               *flagsp = CE_RECV_FLAG_SWAPPED;
+       else
+               *flagsp = 0;
+
+       if (per_transfer_contextp)
+               *per_transfer_contextp =
+                       dest_ring->per_transfer_context[sw_index];
+
+       /* sanity */
+       dest_ring->per_transfer_context[sw_index] = NULL;
+
+       /* Update sw_index */
+       sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+       dest_ring->sw_index = sw_index;
+
+       return 0;
+}
+
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp,
+                                 unsigned int *flagsp)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_completed_recv_next_nolock(ce_state,
+                                                  per_transfer_contextp,
+                                                  bufferp, nbytesp,
+                                                  transfer_idp, flagsp);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp)
+{
+       struct ce_ring_state *dest_ring;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int ret;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+
+       dest_ring = ce_state->dest_ring;
+
+       if (!dest_ring)
+               return -EIO;
+
+       ar = ce_state->ar;
+       ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       nentries_mask = dest_ring->nentries_mask;
+       sw_index = dest_ring->sw_index;
+       write_index = dest_ring->write_index;
+       if (write_index != sw_index) {
+               struct ce_desc *base = dest_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+
+               /* Return data from completed destination descriptor */
+               *bufferp = __le32_to_cpu(desc->addr);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               dest_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               dest_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               dest_ring->sw_index = sw_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_send_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
+                                               void **per_transfer_contextp,
+                                               u32 *bufferp,
+                                               unsigned int *nbytesp,
+                                               unsigned int *transfer_idp)
+{
+       struct ce_ring_state *src_ring = ce_state->src_ring;
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+       unsigned int nentries_mask = src_ring->nentries_mask;
+       unsigned int sw_index = src_ring->sw_index;
+       unsigned int read_index;
+       int ret = -EIO;
+
+       if (src_ring->hw_index == sw_index) {
+               /*
+                * The SW completion index has caught up with the cached
+                * version of the HW completion index.
+                * Update the cached HW completion index to see whether
+                * the SW has really caught up to the HW, or if the cached
+                * value of the HW index has become stale.
+                */
+               ath10k_pci_wake(ar);
+               src_ring->hw_index =
+                       ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               ath10k_pci_sleep(ar);
+       }
+       read_index = src_ring->hw_index;
+
+       if ((read_index != sw_index) && (read_index != 0xffffffff)) {
+               struct ce_desc *sbase = src_ring->shadow_base;
+               struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
+
+               /* Return data from completed source descriptor */
+               *bufferp = __le32_to_cpu(sdesc->addr);
+               *nbytesp = __le16_to_cpu(sdesc->nbytes);
+               *transfer_idp = MS(__le16_to_cpu(sdesc->flags),
+                                               CE_DESC_FLAGS_META_DATA);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               src_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               src_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               src_ring->sw_index = sw_index;
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/* NB: Modeled after ath10k_ce_completed_send_next */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp,
+                              unsigned int *nbytesp,
+                              unsigned int *transfer_idp)
+{
+       struct ce_ring_state *src_ring;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int ret;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+
+       src_ring = ce_state->src_ring;
+
+       if (!src_ring)
+               return -EIO;
+
+       ar = ce_state->ar;
+       ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       nentries_mask = src_ring->nentries_mask;
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
+       if (write_index != sw_index) {
+               struct ce_desc *base = src_ring->base_addr_owner_space;
+               struct ce_desc *desc = CE_SRC_RING_TO_DESC(base, sw_index);
+
+               /* Return data from completed source descriptor */
+               *bufferp = __le32_to_cpu(desc->addr);
+               *nbytesp = __le16_to_cpu(desc->nbytes);
+               *transfer_idp = MS(__le16_to_cpu(desc->flags),
+                                               CE_DESC_FLAGS_META_DATA);
+
+               if (per_transfer_contextp)
+                       *per_transfer_contextp =
+                               src_ring->per_transfer_context[sw_index];
+
+               /* sanity */
+               src_ring->per_transfer_context[sw_index] = NULL;
+
+               /* Update sw_index */
+               sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+               src_ring->sw_index = sw_index;
+               ret = 0;
+       } else {
+               ret = -EIO;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ret = ath10k_ce_completed_send_next_nolock(ce_state,
+                                                  per_transfer_contextp,
+                                                  bufferp, nbytesp,
+                                                  transfer_idp);
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ret;
+}
+
+/*
+ * Guts of interrupt handler for per-engine interrupts on a particular CE.
+ *
+ * Invokes registered callbacks for recv_complete,
+ * send_complete, and watermarks.
+ */
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       void *transfer_context;
+       u32 buf;
+       unsigned int nbytes;
+       unsigned int id;
+       unsigned int flags;
+
+       ath10k_pci_wake(ar);
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       /* Clear the copy-complete interrupts that will be handled here. */
+       ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
+                                         HOST_IS_COPY_COMPLETE_MASK);
+
+       if (ce_state->recv_cb) {
+               /*
+                * Pop completed recv buffers and call the registered
+                * recv callback for each
+                */
+               while (ath10k_ce_completed_recv_next_nolock(ce_state,
+                                                           &transfer_context,
+                                                           &buf, &nbytes,
+                                                           &id, &flags) == 0) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       ce_state->recv_cb(ce_state, transfer_context, buf,
+                                         nbytes, id, flags);
+                       spin_lock_bh(&ar_pci->ce_lock);
+               }
+       }
+
+       if (ce_state->send_cb) {
+               /*
+                * Pop completed send buffers and call the registered
+                * send callback for each
+                */
+               while (ath10k_ce_completed_send_next_nolock(ce_state,
+                                                           &transfer_context,
+                                                           &buf,
+                                                           &nbytes,
+                                                           &id) == 0) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       ce_state->send_cb(ce_state, transfer_context,
+                                         buf, nbytes, id);
+                       spin_lock_bh(&ar_pci->ce_lock);
+               }
+       }
+
+       /*
+        * Misc CE interrupts are not being handled, but still need
+        * to be cleared.
+        */
+       ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+       ath10k_pci_sleep(ar);
+}
+
+/*
+ * Handler for per-engine interrupts on ALL active CEs.
+ * This is used in cases where the system is sharing a
+ * single interrput for all CEs
+ */
+
+void ath10k_ce_per_engine_service_any(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id;
+       u32 intr_summary;
+
+       ath10k_pci_wake(ar);
+       intr_summary = CE_INTERRUPT_SUMMARY(ar);
+
+       for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) {
+               if (intr_summary & (1 << ce_id))
+                       intr_summary &= ~(1 << ce_id);
+               else
+                       /* no intr pending on this CE */
+                       continue;
+
+               ath10k_ce_per_engine_service(ar, ce_id);
+       }
+
+       ath10k_pci_sleep(ar);
+}
+
+/*
+ * Adjust interrupts for the copy complete handler.
+ * If it's needed for either send or recv, then unmask
+ * this interrupt; otherwise, mask it.
+ *
+ * Called with ce_lock held.
+ */
+static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
+                                               int disable_copy_compl_intr)
+{
+       u32 ctrl_addr = ce_state->ctrl_addr;
+       struct ath10k *ar = ce_state->ar;
+
+       ath10k_pci_wake(ar);
+
+       if ((!disable_copy_compl_intr) &&
+           (ce_state->send_cb || ce_state->recv_cb))
+               ath10k_ce_copy_complete_inter_enable(ar, ctrl_addr);
+       else
+               ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+
+       ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+
+       ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_disable_interrupts(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id;
+
+       ath10k_pci_wake(ar);
+       for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) {
+               struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+               u32 ctrl_addr = ce_state->ctrl_addr;
+
+               ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+       }
+       ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+                               void (*send_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id),
+                               int disable_interrupts)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->send_cb = send_cb;
+       ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts);
+       spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+                               void (*recv_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id,
+                                                unsigned int flags))
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->recv_cb = recv_cb;
+       ath10k_ce_per_engine_handler_adjust(ce_state, 0);
+       spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+static int ath10k_ce_init_src_ring(struct ath10k *ar,
+                                  unsigned int ce_id,
+                                  struct ce_state *ce_state,
+                                  const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_ring_state *src_ring;
+       unsigned int nentries = attr->src_nentries;
+       unsigned int ce_nbytes;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       dma_addr_t base_addr;
+       char *ptr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       if (ce_state->src_ring) {
+               WARN_ON(ce_state->src_ring->nentries != nentries);
+               return 0;
+       }
+
+       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       ce_state->src_ring = (struct ce_ring_state *)ptr;
+       src_ring = ce_state->src_ring;
+
+       ptr += sizeof(struct ce_ring_state);
+       src_ring->nentries = nentries;
+       src_ring->nentries_mask = nentries - 1;
+
+       ath10k_pci_wake(ar);
+       src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+       src_ring->hw_index = src_ring->sw_index;
+
+       src_ring->write_index =
+               ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       src_ring->per_transfer_context = (void **)ptr;
+
+       /*
+        * Legacy platforms that do not support cache
+        * coherent DMA are unsupported
+        */
+       src_ring->base_addr_owner_space_unaligned =
+               pci_alloc_consistent(ar_pci->pdev,
+                                    (nentries * sizeof(struct ce_desc) +
+                                     CE_DESC_RING_ALIGN),
+                                    &base_addr);
+       src_ring->base_addr_ce_space_unaligned = base_addr;
+
+       src_ring->base_addr_owner_space = PTR_ALIGN(
+                       src_ring->base_addr_owner_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+       src_ring->base_addr_ce_space = ALIGN(
+                       src_ring->base_addr_ce_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       /*
+        * Also allocate a shadow src ring in regular
+        * mem to use for faster access.
+        */
+       src_ring->shadow_base_unaligned =
+               kmalloc((nentries * sizeof(struct ce_desc) +
+                        CE_DESC_RING_ALIGN), GFP_KERNEL);
+
+       src_ring->shadow_base = PTR_ALIGN(
+                       src_ring->shadow_base_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       ath10k_pci_wake(ar);
+       ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr,
+                                        src_ring->base_addr_ce_space);
+       ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries);
+       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max);
+       ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static int ath10k_ce_init_dest_ring(struct ath10k *ar,
+                                   unsigned int ce_id,
+                                   struct ce_state *ce_state,
+                                   const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_ring_state *dest_ring;
+       unsigned int nentries = attr->dest_nentries;
+       unsigned int ce_nbytes;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+       dma_addr_t base_addr;
+       char *ptr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       if (ce_state->dest_ring) {
+               WARN_ON(ce_state->dest_ring->nentries != nentries);
+               return 0;
+       }
+
+       ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+       if (ptr == NULL)
+               return -ENOMEM;
+
+       ce_state->dest_ring = (struct ce_ring_state *)ptr;
+       dest_ring = ce_state->dest_ring;
+
+       ptr += sizeof(struct ce_ring_state);
+       dest_ring->nentries = nentries;
+       dest_ring->nentries_mask = nentries - 1;
+
+       ath10k_pci_wake(ar);
+       dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+       dest_ring->write_index =
+               ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       dest_ring->per_transfer_context = (void **)ptr;
+
+       /*
+        * Legacy platforms that do not support cache
+        * coherent DMA are unsupported
+        */
+       dest_ring->base_addr_owner_space_unaligned =
+               pci_alloc_consistent(ar_pci->pdev,
+                                    (nentries * sizeof(struct ce_desc) +
+                                     CE_DESC_RING_ALIGN),
+                                    &base_addr);
+       dest_ring->base_addr_ce_space_unaligned = base_addr;
+
+       /*
+        * Correctly initialize memory to 0 to prevent garbage
+        * data crashing system when download firmware
+        */
+       memset(dest_ring->base_addr_owner_space_unaligned, 0,
+              nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN);
+
+       dest_ring->base_addr_owner_space = PTR_ALIGN(
+                       dest_ring->base_addr_owner_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+       dest_ring->base_addr_ce_space = ALIGN(
+                       dest_ring->base_addr_ce_space_unaligned,
+                       CE_DESC_RING_ALIGN);
+
+       ath10k_pci_wake(ar);
+       ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr,
+                                         dest_ring->base_addr_ce_space);
+       ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries);
+       ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
+                                            unsigned int ce_id,
+                                            const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = NULL;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       spin_lock_bh(&ar_pci->ce_lock);
+
+       if (!ar_pci->ce_id_to_state[ce_id]) {
+               ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC);
+               if (ce_state == NULL) {
+                       spin_unlock_bh(&ar_pci->ce_lock);
+                       return NULL;
+               }
+
+               ar_pci->ce_id_to_state[ce_id] = ce_state;
+               ce_state->ar = ar;
+               ce_state->id = ce_id;
+               ce_state->ctrl_addr = ctrl_addr;
+               ce_state->state = CE_RUNNING;
+               /* Save attribute flags */
+               ce_state->attr_flags = attr->flags;
+               ce_state->src_sz_max = attr->src_sz_max;
+       }
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+
+       return ce_state;
+}
+
+/*
+ * Initialize a Copy Engine based on caller-supplied attributes.
+ * This may be called once to initialize both source and destination
+ * rings or it may be called twice for separate source and destination
+ * initialization. It may be that only one side or the other is
+ * initialized by software/firmware.
+ */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+                               unsigned int ce_id,
+                               const struct ce_attr *attr)
+{
+       struct ce_state *ce_state;
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ce_state = ath10k_ce_init_state(ar, ce_id, attr);
+       if (!ce_state) {
+               ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id);
+               return NULL;
+       }
+
+       if (attr->src_nentries) {
+               if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) {
+                       ath10k_err("Failed to initialize CE src ring for ID: %d\n",
+                                  ce_id);
+                       ath10k_ce_deinit(ce_state);
+                       return NULL;
+               }
+       }
+
+       if (attr->dest_nentries) {
+               if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) {
+                       ath10k_err("Failed to initialize CE dest ring for ID: %d\n",
+                                  ce_id);
+                       ath10k_ce_deinit(ce_state);
+                       return NULL;
+               }
+       }
+
+       /* Enable CE error interrupts */
+       ath10k_pci_wake(ar);
+       ath10k_ce_error_intr_enable(ar, ctrl_addr);
+       ath10k_pci_sleep(ar);
+
+       return ce_state;
+}
+
+void ath10k_ce_deinit(struct ce_state *ce_state)
+{
+       unsigned int ce_id = ce_state->id;
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ce_state->state = CE_UNUSED;
+       ar_pci->ce_id_to_state[ce_id] = NULL;
+
+       if (ce_state->src_ring) {
+               kfree(ce_state->src_ring->shadow_base_unaligned);
+               pci_free_consistent(ar_pci->pdev,
+                                   (ce_state->src_ring->nentries *
+                                    sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   ce_state->src_ring->base_addr_owner_space,
+                                   ce_state->src_ring->base_addr_ce_space);
+               kfree(ce_state->src_ring);
+       }
+
+       if (ce_state->dest_ring) {
+               pci_free_consistent(ar_pci->pdev,
+                                   (ce_state->dest_ring->nentries *
+                                    sizeof(struct ce_desc) +
+                                    CE_DESC_RING_ALIGN),
+                                   ce_state->dest_ring->base_addr_owner_space,
+                                   ce_state->dest_ring->base_addr_ce_space);
+               kfree(ce_state->dest_ring);
+       }
+       kfree(ce_state);
+}
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
new file mode 100644 (file)
index 0000000..c17f07c
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _CE_H_
+#define _CE_H_
+
+#include "hif.h"
+
+
+/* Maximum number of Copy Engine's supported */
+#define CE_COUNT_MAX 8
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+
+/* Descriptor rings must be aligned to this boundary */
+#define CE_DESC_RING_ALIGN     8
+#define CE_SENDLIST_ITEMS_MAX  12
+#define CE_SEND_FLAG_GATHER    0x00010000
+
+/*
+ * Copy Engine support: low-level Target-side Copy Engine API.
+ * This is a hardware access layer used by code that understands
+ * how to use copy engines.
+ */
+
+struct ce_state;
+
+
+/* Copy Engine operational state */
+enum ce_op_state {
+       CE_UNUSED,
+       CE_PAUSED,
+       CE_RUNNING,
+};
+
+#define CE_DESC_FLAGS_GATHER         (1 << 0)
+#define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
+#define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
+#define CE_DESC_FLAGS_META_DATA_LSB  3
+
+struct ce_desc {
+       __le32 addr;
+       __le16 nbytes;
+       __le16 flags; /* %CE_DESC_FLAGS_ */
+};
+
+/* Copy Engine Ring internal state */
+struct ce_ring_state {
+       /* Number of entries in this ring; must be power of 2 */
+       unsigned int nentries;
+       unsigned int nentries_mask;
+
+       /*
+        * For dest ring, this is the next index to be processed
+        * by software after it was/is received into.
+        *
+        * For src ring, this is the last descriptor that was sent
+        * and completion processed by software.
+        *
+        * Regardless of src or dest ring, this is an invariant
+        * (modulo ring size):
+        *     write index >= read index >= sw_index
+        */
+       unsigned int sw_index;
+       /* cached copy */
+       unsigned int write_index;
+       /*
+        * For src ring, this is the next index not yet processed by HW.
+        * This is a cached copy of the real HW index (read index), used
+        * for avoiding reading the HW index register more often than
+        * necessary.
+        * This extends the invariant:
+        *     write index >= read index >= hw_index >= sw_index
+        *
+        * For dest ring, this is currently unused.
+        */
+       /* cached copy */
+       unsigned int hw_index;
+
+       /* Start of DMA-coherent area reserved for descriptors */
+       /* Host address space */
+       void *base_addr_owner_space_unaligned;
+       /* CE address space */
+       u32 base_addr_ce_space_unaligned;
+
+       /*
+        * Actual start of descriptors.
+        * Aligned to descriptor-size boundary.
+        * Points into reserved DMA-coherent area, above.
+        */
+       /* Host address space */
+       void *base_addr_owner_space;
+
+       /* CE address space */
+       u32 base_addr_ce_space;
+       /*
+        * Start of shadow copy of descriptors, within regular memory.
+        * Aligned to descriptor-size boundary.
+        */
+       void *shadow_base_unaligned;
+       struct ce_desc *shadow_base;
+
+       void **per_transfer_context;
+};
+
+/* Copy Engine internal state */
+struct ce_state {
+       struct ath10k *ar;
+       unsigned int id;
+
+       unsigned int attr_flags;
+
+       u32 ctrl_addr;
+       enum ce_op_state state;
+
+       void (*send_cb) (struct ce_state *ce_state,
+                        void *per_transfer_send_context,
+                        u32 buffer,
+                        unsigned int nbytes,
+                        unsigned int transfer_id);
+       void (*recv_cb) (struct ce_state *ce_state,
+                        void *per_transfer_recv_context,
+                        u32 buffer,
+                        unsigned int nbytes,
+                        unsigned int transfer_id,
+                        unsigned int flags);
+
+       unsigned int src_sz_max;
+       struct ce_ring_state *src_ring;
+       struct ce_ring_state *dest_ring;
+};
+
+struct ce_sendlist_item {
+       /* e.g. buffer or desc list */
+       dma_addr_t data;
+       union {
+               /* simple buffer */
+               unsigned int nbytes;
+               /* Rx descriptor list */
+               unsigned int ndesc;
+       } u;
+       /* externally-specified flags; OR-ed with internal flags */
+       u32 flags;
+};
+
+struct ce_sendlist {
+       unsigned int num_items;
+       struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
+};
+
+/* Copy Engine settable attributes */
+struct ce_attr;
+
+/*==================Send====================*/
+
+/* ath10k_ce_send flags */
+#define CE_SEND_FLAG_BYTE_SWAP 1
+
+/*
+ * Queue a source buffer to be sent to an anonymous destination buffer.
+ *   ce         - which copy engine to use
+ *   buffer          - address of buffer
+ *   nbytes          - number of bytes to send
+ *   transfer_id     - arbitrary ID; reflected to destination
+ *   flags           - CE_SEND_FLAG_* values
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Note: If no flags are specified, use CE's default data swap mode.
+ *
+ * Implementation note: pushes 1 buffer to Source ring
+ */
+int ath10k_ce_send(struct ce_state *ce_state,
+                  void *per_transfer_send_context,
+                  u32 buffer,
+                  unsigned int nbytes,
+                  /* 14 bits */
+                  unsigned int transfer_id,
+                  unsigned int flags);
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+                               void (*send_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id),
+                               int disable_interrupts);
+
+/* Append a simple buffer (address/length) to a sendlist. */
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
+                               u32 buffer,
+                               unsigned int nbytes,
+                               /* OR-ed with internal flags */
+                               u32 flags);
+
+/*
+ * Queue a "sendlist" of buffers to be sent using gather to a single
+ * anonymous destination buffer
+ *   ce         - which copy engine to use
+ *   sendlist        - list of simple buffers to send using gather
+ *   transfer_id     - arbitrary ID; reflected to destination
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes multiple buffers with Gather to Source ring.
+ */
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+                           void *per_transfer_send_context,
+                           struct ce_sendlist *sendlist,
+                           /* 14 bits */
+                           unsigned int transfer_id);
+
+/*==================Recv=======================*/
+
+/*
+ * Make a buffer available to receive. The buffer must be at least of a
+ * minimal size appropriate for this copy engine (src_sz_max attribute).
+ *   ce                    - which copy engine to use
+ *   per_transfer_recv_context  - context passed back to caller's recv_cb
+ *   buffer                     - address of buffer in CE space
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes a buffer to Dest ring.
+ */
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+                              void *per_transfer_recv_context,
+                              u32 buffer);
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+                               void (*recv_cb) (struct ce_state *ce_state,
+                                                void *transfer_context,
+                                                u32 buffer,
+                                                unsigned int nbytes,
+                                                unsigned int transfer_id,
+                                                unsigned int flags));
+
+/* recv flags */
+/* Data is byte-swapped */
+#define CE_RECV_FLAG_SWAPPED   1
+
+/*
+ * Supply data for the next completed unprocessed receive descriptor.
+ * Pops buffer from Dest ring.
+ */
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+                                 void **per_transfer_contextp,
+                                 u32 *bufferp,
+                                 unsigned int *nbytesp,
+                                 unsigned int *transfer_idp,
+                                 unsigned int *flagsp);
+/*
+ * Supply data for the next completed unprocessed send descriptor.
+ * Pops 1 completed send buffer from Source ring.
+ */
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+                          void **per_transfer_contextp,
+                          u32 *bufferp,
+                          unsigned int *nbytesp,
+                          unsigned int *transfer_idp);
+
+/*==================CE Engine Initialization=======================*/
+
+/* Initialize an instance of a CE */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+                               unsigned int ce_id,
+                               const struct ce_attr *attr);
+
+/*==================CE Engine Shutdown=======================*/
+/*
+ * Support clean shutdown by allowing the caller to revoke
+ * receive buffers.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp);
+
+/*
+ * Support clean shutdown by allowing the caller to cancel
+ * pending sends.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+                              void **per_transfer_contextp,
+                              u32 *bufferp,
+                              unsigned int *nbytesp,
+                              unsigned int *transfer_idp);
+
+void ath10k_ce_deinit(struct ce_state *ce_state);
+
+/*==================CE Interrupt Handlers====================*/
+void ath10k_ce_per_engine_service_any(struct ath10k *ar);
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
+void ath10k_ce_disable_interrupts(struct ath10k *ar);
+
+/* ce_attr.flags values */
+/* Use NonSnooping PCIe accesses? */
+#define CE_ATTR_NO_SNOOP               1
+
+/* Byte swap data words */
+#define CE_ATTR_BYTE_SWAP_DATA         2
+
+/* Swizzle descriptors? */
+#define CE_ATTR_SWIZZLE_DESCRIPTORS    4
+
+/* no interrupt on copy completion */
+#define CE_ATTR_DIS_INTR               8
+
+/* Attributes of an instance of a Copy Engine */
+struct ce_attr {
+       /* CE_ATTR_* values */
+       unsigned int flags;
+
+       /* currently not in use */
+       unsigned int priority;
+
+       /* #entries in source ring - Must be a power of 2 */
+       unsigned int src_nentries;
+
+       /*
+        * Max source send size for this CE.
+        * This is also the minimum size of a destination buffer.
+        */
+       unsigned int src_sz_max;
+
+       /* #entries in destination ring - Must be a power of 2 */
+       unsigned int dest_nentries;
+
+       /* Future use */
+       void *reserved;
+};
+
+/*
+ * When using sendlist_send to transfer multiple buffer fragments, the
+ * transfer context of each fragment, except last one, will be filled
+ * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
+ * each fragment done with send and the transfer context would be
+ * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
+ * status of a send completion.
+ */
+#define CE_SENDLIST_ITEM_CTXT  ((void *)0xcecebeef)
+
+#define SR_BA_ADDRESS          0x0000
+#define SR_SIZE_ADDRESS                0x0004
+#define DR_BA_ADDRESS          0x0008
+#define DR_SIZE_ADDRESS                0x000c
+#define CE_CMD_ADDRESS         0x0018
+
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB     17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB     17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK    0x00020000
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
+       (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
+       CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB     16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB     16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK    0x00010000
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
+       (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
+        CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
+       (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
+        CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_DMAX_LENGTH_MSB               15
+#define CE_CTRL1_DMAX_LENGTH_LSB               0
+#define CE_CTRL1_DMAX_LENGTH_MASK              0x0000ffff
+#define CE_CTRL1_DMAX_LENGTH_GET(x) \
+       (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
+#define CE_CTRL1_DMAX_LENGTH_SET(x) \
+       (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
+
+#define CE_CTRL1_ADDRESS                       0x0010
+#define CE_CTRL1_HW_MASK                       0x0007ffff
+#define CE_CTRL1_SW_MASK                       0x0007ffff
+#define CE_CTRL1_HW_WRITE_MASK                 0x00000000
+#define CE_CTRL1_SW_WRITE_MASK                 0x0007ffff
+#define CE_CTRL1_RSTMASK                       0xffffffff
+#define CE_CTRL1_RESET                         0x00000080
+
+#define CE_CMD_HALT_STATUS_MSB                 3
+#define CE_CMD_HALT_STATUS_LSB                 3
+#define CE_CMD_HALT_STATUS_MASK                        0x00000008
+#define CE_CMD_HALT_STATUS_GET(x) \
+       (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB)
+#define CE_CMD_HALT_STATUS_SET(x) \
+       (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK)
+#define CE_CMD_HALT_STATUS_RESET               0
+#define CE_CMD_HALT_MSB                                0
+#define CE_CMD_HALT_MASK                       0x00000001
+
+#define HOST_IE_COPY_COMPLETE_MSB              0
+#define HOST_IE_COPY_COMPLETE_LSB              0
+#define HOST_IE_COPY_COMPLETE_MASK             0x00000001
+#define HOST_IE_COPY_COMPLETE_GET(x) \
+       (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB)
+#define HOST_IE_COPY_COMPLETE_SET(x) \
+       (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK)
+#define HOST_IE_COPY_COMPLETE_RESET            0
+#define HOST_IE_ADDRESS                                0x002c
+
+#define HOST_IS_DST_RING_LOW_WATERMARK_MASK    0x00000010
+#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK   0x00000008
+#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK    0x00000004
+#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK   0x00000002
+#define HOST_IS_COPY_COMPLETE_MASK             0x00000001
+#define HOST_IS_ADDRESS                                0x0030
+
+#define MISC_IE_ADDRESS                                0x0034
+
+#define MISC_IS_AXI_ERR_MASK                   0x00000400
+
+#define MISC_IS_DST_ADDR_ERR_MASK              0x00000200
+#define MISC_IS_SRC_LEN_ERR_MASK               0x00000100
+#define MISC_IS_DST_MAX_LEN_VIO_MASK           0x00000080
+#define MISC_IS_DST_RING_OVERFLOW_MASK         0x00000040
+#define MISC_IS_SRC_RING_OVERFLOW_MASK         0x00000020
+
+#define MISC_IS_ADDRESS                                0x0038
+
+#define SR_WR_INDEX_ADDRESS                    0x003c
+
+#define DST_WR_INDEX_ADDRESS                   0x0040
+
+#define CURRENT_SRRI_ADDRESS                   0x0044
+
+#define CURRENT_DRRI_ADDRESS                   0x0048
+
+#define SRC_WATERMARK_LOW_MSB                  31
+#define SRC_WATERMARK_LOW_LSB                  16
+#define SRC_WATERMARK_LOW_MASK                 0xffff0000
+#define SRC_WATERMARK_LOW_GET(x) \
+       (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
+#define SRC_WATERMARK_LOW_SET(x) \
+       (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
+#define SRC_WATERMARK_LOW_RESET                        0
+#define SRC_WATERMARK_HIGH_MSB                 15
+#define SRC_WATERMARK_HIGH_LSB                 0
+#define SRC_WATERMARK_HIGH_MASK                        0x0000ffff
+#define SRC_WATERMARK_HIGH_GET(x) \
+       (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
+#define SRC_WATERMARK_HIGH_SET(x) \
+       (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
+#define SRC_WATERMARK_HIGH_RESET               0
+#define SRC_WATERMARK_ADDRESS                  0x004c
+
+#define DST_WATERMARK_LOW_LSB                  16
+#define DST_WATERMARK_LOW_MASK                 0xffff0000
+#define DST_WATERMARK_LOW_SET(x) \
+       (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
+#define DST_WATERMARK_LOW_RESET                        0
+#define DST_WATERMARK_HIGH_MSB                 15
+#define DST_WATERMARK_HIGH_LSB                 0
+#define DST_WATERMARK_HIGH_MASK                        0x0000ffff
+#define DST_WATERMARK_HIGH_GET(x) \
+       (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
+#define DST_WATERMARK_HIGH_SET(x) \
+       (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
+#define DST_WATERMARK_HIGH_RESET               0
+#define DST_WATERMARK_ADDRESS                  0x0050
+
+
+static inline u32 ath10k_ce_base_address(unsigned int ce_id)
+{
+       return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
+}
+
+#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK  | \
+                          HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
+                          HOST_IS_DST_RING_LOW_WATERMARK_MASK  | \
+                          HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
+
+#define CE_ERROR_MASK  (MISC_IS_AXI_ERR_MASK           | \
+                        MISC_IS_DST_ADDR_ERR_MASK      | \
+                        MISC_IS_SRC_LEN_ERR_MASK       | \
+                        MISC_IS_DST_MAX_LEN_VIO_MASK   | \
+                        MISC_IS_DST_RING_OVERFLOW_MASK | \
+                        MISC_IS_SRC_RING_OVERFLOW_MASK)
+
+#define CE_SRC_RING_TO_DESC(baddr, idx) \
+       (&(((struct ce_desc *)baddr)[idx]))
+
+#define CE_DEST_RING_TO_DESC(baddr, idx) \
+       (&(((struct ce_desc *)baddr)[idx]))
+
+/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */
+#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \
+       (((int)(toidx)-(int)(fromidx)) & (nentries_mask))
+
+#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
+
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB              8
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK             0x0000ff00
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \
+       (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
+               CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS                   0x0000
+
+#define CE_INTERRUPT_SUMMARY(ar) \
+       CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
+               ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \
+               CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
+
+#endif /* _CE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
new file mode 100644 (file)
index 0000000..2b3426b
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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/module.h>
+#include <linux/firmware.h>
+
+#include "core.h"
+#include "mac.h"
+#include "htc.h"
+#include "hif.h"
+#include "wmi.h"
+#include "bmi.h"
+#include "debug.h"
+#include "htt.h"
+
+unsigned int ath10k_debug_mask;
+static bool uart_print;
+static unsigned int ath10k_p2p;
+module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
+module_param(uart_print, bool, 0644);
+module_param_named(p2p, ath10k_p2p, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+MODULE_PARM_DESC(uart_print, "Uart target debugging");
+MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
+
+static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+       {
+               .id = QCA988X_HW_1_0_VERSION,
+               .name = "qca988x hw1.0",
+               .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR,
+               .fw = {
+                       .dir = QCA988X_HW_1_0_FW_DIR,
+                       .fw = QCA988X_HW_1_0_FW_FILE,
+                       .otp = QCA988X_HW_1_0_OTP_FILE,
+                       .board = QCA988X_HW_1_0_BOARD_DATA_FILE,
+               },
+       },
+       {
+               .id = QCA988X_HW_2_0_VERSION,
+               .name = "qca988x hw2.0",
+               .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
+               .fw = {
+                       .dir = QCA988X_HW_2_0_FW_DIR,
+                       .fw = QCA988X_HW_2_0_FW_FILE,
+                       .otp = QCA988X_HW_2_0_OTP_FILE,
+                       .board = QCA988X_HW_2_0_BOARD_DATA_FILE,
+               },
+       },
+};
+
+static void ath10k_send_suspend_complete(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
+
+       ar->is_target_paused = true;
+       wake_up(&ar->event_queue);
+}
+
+static int ath10k_check_fw_version(struct ath10k *ar)
+{
+       char version[32];
+
+       if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
+           ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
+           ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
+           ar->fw_version_build >= SUPPORTED_FW_BUILD)
+               return 0;
+
+       snprintf(version, sizeof(version), "%u.%u.%u.%u",
+                SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
+                SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
+
+       ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
+                   ar->hw->wiphy->fw_version);
+       ath10k_warn("Please upgrade to version %s (or newer)\n", version);
+
+       return 0;
+}
+
+static int ath10k_init_connect_htc(struct ath10k *ar)
+{
+       int status;
+
+       status = ath10k_wmi_connect_htc_service(ar);
+       if (status)
+               goto conn_fail;
+
+       /* Start HTC */
+       status = ath10k_htc_start(ar->htc);
+       if (status)
+               goto conn_fail;
+
+       /* Wait for WMI event to be ready */
+       status = ath10k_wmi_wait_for_service_ready(ar);
+       if (status <= 0) {
+               ath10k_warn("wmi service ready event not received");
+               status = -ETIMEDOUT;
+               goto timeout;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
+       return 0;
+
+timeout:
+       ath10k_htc_stop(ar->htc);
+conn_fail:
+       return status;
+}
+
+static int ath10k_init_configure_target(struct ath10k *ar)
+{
+       u32 param_host;
+       int ret;
+
+       /* tell target which HTC version it is used*/
+       ret = ath10k_bmi_write32(ar, hi_app_host_interest,
+                                HTC_PROTOCOL_VERSION);
+       if (ret) {
+               ath10k_err("settings HTC version failed\n");
+               return ret;
+       }
+
+       /* set the firmware mode to STA/IBSS/AP */
+       ret = ath10k_bmi_read32(ar, hi_option_flag, &param_host);
+       if (ret) {
+               ath10k_err("setting firmware mode (1/2) failed\n");
+               return ret;
+       }
+
+       /* TODO following parameters need to be re-visited. */
+       /* num_device */
+       param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT);
+       /* Firmware mode */
+       /* FIXME: Why FW_MODE_AP ??.*/
+       param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT);
+       /* mac_addr_method */
+       param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
+       /* firmware_bridge */
+       param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
+       /* fwsubmode */
+       param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT);
+
+       ret = ath10k_bmi_write32(ar, hi_option_flag, param_host);
+       if (ret) {
+               ath10k_err("setting firmware mode (2/2) failed\n");
+               return ret;
+       }
+
+       /* We do all byte-swapping on the host */
+       ret = ath10k_bmi_write32(ar, hi_be, 0);
+       if (ret) {
+               ath10k_err("setting host CPU BE mode failed\n");
+               return ret;
+       }
+
+       /* FW descriptor/Data swap flags */
+       ret = ath10k_bmi_write32(ar, hi_fw_swap, 0);
+
+       if (ret) {
+               ath10k_err("setting FW data/desc swap flags failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+                                                  const char *dir,
+                                                  const char *file)
+{
+       char filename[100];
+       const struct firmware *fw;
+       int ret;
+
+       if (file == NULL)
+               return ERR_PTR(-ENOENT);
+
+       if (dir == NULL)
+               dir = ".";
+
+       snprintf(filename, sizeof(filename), "%s/%s", dir, file);
+       ret = request_firmware(&fw, filename, ar->dev);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return fw;
+}
+
+static int ath10k_push_board_ext_data(struct ath10k *ar,
+                                     const struct firmware *fw)
+{
+       u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+       u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
+       u32 board_ext_data_addr;
+       int ret;
+
+       ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr);
+       if (ret) {
+               ath10k_err("could not read board ext data addr (%d)\n", ret);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_CORE,
+                  "ath10k: Board extended Data download addr: 0x%x\n",
+                  board_ext_data_addr);
+
+       if (board_ext_data_addr == 0)
+               return 0;
+
+       if (fw->size != (board_data_size + board_ext_data_size)) {
+               ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
+                          fw->size, board_data_size, board_ext_data_size);
+               return -EINVAL;
+       }
+
+       ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
+                                     fw->data + board_data_size,
+                                     board_ext_data_size);
+       if (ret) {
+               ath10k_err("could not write board ext data (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
+                                (board_ext_data_size << 16) | 1);
+       if (ret) {
+               ath10k_err("could not write board ext data bit (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_download_board_data(struct ath10k *ar)
+{
+       u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+       u32 address;
+       const struct firmware *fw;
+       int ret;
+
+       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+                                 ar->hw_params.fw.board);
+       if (IS_ERR(fw)) {
+               ath10k_err("could not fetch board data fw file (%ld)\n",
+                          PTR_ERR(fw));
+               return PTR_ERR(fw);
+       }
+
+       ret = ath10k_push_board_ext_data(ar, fw);
+       if (ret) {
+               ath10k_err("could not push board ext data (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_read32(ar, hi_board_data, &address);
+       if (ret) {
+               ath10k_err("could not read board data addr (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_write_memory(ar, address, fw->data,
+                                     min_t(u32, board_data_size, fw->size));
+       if (ret) {
+               ath10k_err("could not write board data (%d)\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1);
+       if (ret) {
+               ath10k_err("could not write board data bit (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_download_and_run_otp(struct ath10k *ar)
+{
+       const struct firmware *fw;
+       u32 address;
+       u32 exec_param;
+       int ret;
+
+       /* OTP is optional */
+
+       if (ar->hw_params.fw.otp == NULL) {
+               ath10k_info("otp file not defined\n");
+               return 0;
+       }
+
+       address = ar->hw_params.patch_load_addr;
+
+       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+                                 ar->hw_params.fw.otp);
+       if (IS_ERR(fw)) {
+               ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+               return 0;
+       }
+
+       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       if (ret) {
+               ath10k_err("could not write otp (%d)\n", ret);
+               goto exit;
+       }
+
+       exec_param = 0;
+       ret = ath10k_bmi_execute(ar, address, &exec_param);
+       if (ret) {
+               ath10k_err("could not execute otp (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_download_fw(struct ath10k *ar)
+{
+       const struct firmware *fw;
+       u32 address;
+       int ret;
+
+       if (ar->hw_params.fw.fw == NULL)
+               return -EINVAL;
+
+       address = ar->hw_params.patch_load_addr;
+
+       fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+                                 ar->hw_params.fw.fw);
+       if (IS_ERR(fw)) {
+               ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
+               return PTR_ERR(fw);
+       }
+
+       ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+       if (ret) {
+               ath10k_err("could not write fw (%d)\n", ret);
+               goto exit;
+       }
+
+exit:
+       release_firmware(fw);
+       return ret;
+}
+
+static int ath10k_init_download_firmware(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_download_board_data(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_download_and_run_otp(ar);
+       if (ret)
+               return ret;
+
+       ret = ath10k_download_fw(ar);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int ath10k_init_uart(struct ath10k *ar)
+{
+       int ret;
+
+       /*
+        * Explicitly setting UART prints to zero as target turns it on
+        * based on scratch registers.
+        */
+       ret = ath10k_bmi_write32(ar, hi_serial_enable, 0);
+       if (ret) {
+               ath10k_warn("could not disable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       if (!uart_print) {
+               ath10k_info("UART prints disabled\n");
+               return 0;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
+       if (ret) {
+               ath10k_warn("could not enable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_write32(ar, hi_serial_enable, 1);
+       if (ret) {
+               ath10k_warn("could not enable UART prints (%d)\n", ret);
+               return ret;
+       }
+
+       ath10k_info("UART prints enabled\n");
+       return 0;
+}
+
+static int ath10k_init_hw_params(struct ath10k *ar)
+{
+       const struct ath10k_hw_params *uninitialized_var(hw_params);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
+               hw_params = &ath10k_hw_params_list[i];
+
+               if (hw_params->id == ar->target_version)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(ath10k_hw_params_list)) {
+               ath10k_err("Unsupported hardware version: 0x%x\n",
+                          ar->target_version);
+               return -EINVAL;
+       }
+
+       ar->hw_params = *hw_params;
+
+       ath10k_info("Hardware name %s version 0x%x\n",
+                   ar->hw_params.name, ar->target_version);
+
+       return 0;
+}
+
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+                                 enum ath10k_bus bus,
+                                 const struct ath10k_hif_ops *hif_ops)
+{
+       struct ath10k *ar;
+
+       ar = ath10k_mac_create();
+       if (!ar)
+               return NULL;
+
+       ar->ath_common.priv = ar;
+       ar->ath_common.hw = ar->hw;
+
+       ar->p2p = !!ath10k_p2p;
+       ar->dev = dev;
+
+       ar->hif.priv = hif_priv;
+       ar->hif.ops = hif_ops;
+       ar->hif.bus = bus;
+
+       ar->free_vdev_map = 0xFF; /* 8 vdevs */
+
+       init_completion(&ar->scan.started);
+       init_completion(&ar->scan.completed);
+       init_completion(&ar->scan.on_channel);
+
+       init_completion(&ar->install_key_done);
+       init_completion(&ar->vdev_setup_done);
+
+       setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
+
+       ar->workqueue = create_singlethread_workqueue("ath10k_wq");
+       if (!ar->workqueue)
+               goto err_wq;
+
+       mutex_init(&ar->conf_mutex);
+       spin_lock_init(&ar->data_lock);
+
+       INIT_LIST_HEAD(&ar->peers);
+       init_waitqueue_head(&ar->peer_mapping_wq);
+
+       init_completion(&ar->offchan_tx_completed);
+       INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
+       skb_queue_head_init(&ar->offchan_tx_queue);
+
+       init_waitqueue_head(&ar->event_queue);
+
+       return ar;
+
+err_wq:
+       ath10k_mac_destroy(ar);
+       return NULL;
+}
+EXPORT_SYMBOL(ath10k_core_create);
+
+void ath10k_core_destroy(struct ath10k *ar)
+{
+       flush_workqueue(ar->workqueue);
+       destroy_workqueue(ar->workqueue);
+
+       ath10k_mac_destroy(ar);
+}
+EXPORT_SYMBOL(ath10k_core_destroy);
+
+
+int ath10k_core_register(struct ath10k *ar)
+{
+       struct ath10k_htc_ops htc_ops;
+       struct bmi_target_info target_info;
+       int status;
+
+       memset(&target_info, 0, sizeof(target_info));
+       status = ath10k_bmi_get_target_info(ar, &target_info);
+       if (status)
+               goto err;
+
+       ar->target_version = target_info.version;
+       ar->hw->wiphy->hw_version = target_info.version;
+
+       status = ath10k_init_hw_params(ar);
+       if (status)
+               goto err;
+
+       if (ath10k_init_configure_target(ar)) {
+               status = -EINVAL;
+               goto err;
+       }
+
+       status = ath10k_init_download_firmware(ar);
+       if (status)
+               goto err;
+
+       status = ath10k_init_uart(ar);
+       if (status)
+               goto err;
+
+       htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+
+       ar->htc = ath10k_htc_create(ar, &htc_ops);
+       if (IS_ERR(ar->htc)) {
+               status = PTR_ERR(ar->htc);
+               ath10k_err("could not create HTC (%d)\n", status);
+               goto err;
+       }
+
+       status = ath10k_bmi_done(ar);
+       if (status)
+               goto err_htc_destroy;
+
+       status = ath10k_wmi_attach(ar);
+       if (status) {
+               ath10k_err("WMI attach failed: %d\n", status);
+               goto err_htc_destroy;
+       }
+
+       status = ath10k_htc_wait_target(ar->htc);
+       if (status)
+               goto err_wmi_detach;
+
+       ar->htt = ath10k_htt_attach(ar);
+       if (!ar->htt) {
+               status = -ENOMEM;
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_init_connect_htc(ar);
+       if (status)
+               goto err_htt_detach;
+
+       ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
+
+       status = ath10k_check_fw_version(ar);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_wmi_cmd_init(ar);
+       if (status) {
+               ath10k_err("could not send WMI init command (%d)\n", status);
+               goto err_disconnect_htc;
+       }
+
+       status = ath10k_wmi_wait_for_unified_ready(ar);
+       if (status <= 0) {
+               ath10k_err("wmi unified ready event not received\n");
+               status = -ETIMEDOUT;
+               goto err_disconnect_htc;
+       }
+
+       status = ath10k_htt_attach_target(ar->htt);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_mac_register(ar);
+       if (status)
+               goto err_disconnect_htc;
+
+       status = ath10k_debug_create(ar);
+       if (status) {
+               ath10k_err("unable to initialize debugfs\n");
+               goto err_unregister_mac;
+       }
+
+       return 0;
+
+err_unregister_mac:
+       ath10k_mac_unregister(ar);
+err_disconnect_htc:
+       ath10k_htc_stop(ar->htc);
+err_htt_detach:
+       ath10k_htt_detach(ar->htt);
+err_wmi_detach:
+       ath10k_wmi_detach(ar);
+err_htc_destroy:
+       ath10k_htc_destroy(ar->htc);
+err:
+       return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+       /* We must unregister from mac80211 before we stop HTC and HIF.
+        * Otherwise we will fail to submit commands to FW and mac80211 will be
+        * unhappy about callback failures. */
+       ath10k_mac_unregister(ar);
+       ath10k_htc_stop(ar->htc);
+       ath10k_htt_detach(ar->htt);
+       ath10k_wmi_detach(ar);
+       ath10k_htc_destroy(ar->htc);
+}
+EXPORT_SYMBOL(ath10k_core_unregister);
+
+int ath10k_core_target_suspend(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+       ret = ath10k_wmi_pdev_suspend_target(ar);
+       if (ret)
+               ath10k_warn("could not suspend target (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_suspend);
+
+int ath10k_core_target_resume(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+       ret = ath10k_wmi_pdev_resume_target(ar);
+       if (ret)
+               ath10k_warn("could not resume target (%d)\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_resume);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
new file mode 100644 (file)
index 0000000..539336d
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _CORE_H_
+#define _CORE_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "htc.h"
+#include "hw.h"
+#include "targaddrs.h"
+#include "wmi.h"
+#include "../ath.h"
+#include "../regd.h"
+
+#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
+#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
+#define WO(_f)      ((_f##_OFFSET) >> 2)
+
+#define ATH10K_SCAN_ID 0
+#define WMI_READY_TIMEOUT (5 * HZ)
+#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+
+/* Antenna noise floor */
+#define ATH10K_DEFAULT_NOISE_FLOOR -95
+
+struct ath10k;
+
+enum ath10k_bus {
+       ATH10K_BUS_PCI,
+};
+
+struct ath10k_skb_cb {
+       dma_addr_t paddr;
+       bool is_mapped;
+       bool is_aborted;
+
+       struct {
+               u8 vdev_id;
+               u16 msdu_id;
+               u8 tid;
+               bool is_offchan;
+               bool is_conf;
+               bool discard;
+               bool no_ack;
+               u8 refcount;
+               struct sk_buff *txfrag;
+               struct sk_buff *msdu;
+       } __packed htt;
+
+       /* 4 bytes left on 64bit arch */
+} __packed;
+
+static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
+                    IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+       return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
+}
+
+static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb)
+{
+       if (ATH10K_SKB_CB(skb)->is_mapped)
+               return -EINVAL;
+
+       ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len,
+                                                  DMA_TO_DEVICE);
+
+       if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr)))
+               return -EIO;
+
+       ATH10K_SKB_CB(skb)->is_mapped = true;
+       return 0;
+}
+
+static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb)
+{
+       if (!ATH10K_SKB_CB(skb)->is_mapped)
+               return -EINVAL;
+
+       dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len,
+                        DMA_TO_DEVICE);
+       ATH10K_SKB_CB(skb)->is_mapped = false;
+       return 0;
+}
+
+static inline u32 host_interest_item_address(u32 item_offset)
+{
+       return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
+}
+
+struct ath10k_bmi {
+       bool done_sent;
+};
+
+struct ath10k_wmi {
+       enum ath10k_htc_ep_id eid;
+       struct completion service_ready;
+       struct completion unified_ready;
+       atomic_t pending_tx_count;
+       wait_queue_head_t wq;
+
+       struct sk_buff_head wmi_event_list;
+       struct work_struct wmi_event_work;
+};
+
+struct ath10k_peer_stat {
+       u8 peer_macaddr[ETH_ALEN];
+       u32 peer_rssi;
+       u32 peer_tx_rate;
+};
+
+struct ath10k_target_stats {
+       /* PDEV stats */
+       s32 ch_noise_floor;
+       u32 tx_frame_count;
+       u32 rx_frame_count;
+       u32 rx_clear_count;
+       u32 cycle_count;
+       u32 phy_err_count;
+       u32 chan_tx_power;
+
+       /* PDEV TX stats */
+       s32 comp_queued;
+       s32 comp_delivered;
+       s32 msdu_enqued;
+       s32 mpdu_enqued;
+       s32 wmm_drop;
+       s32 local_enqued;
+       s32 local_freed;
+       s32 hw_queued;
+       s32 hw_reaped;
+       s32 underrun;
+       s32 tx_abort;
+       s32 mpdus_requed;
+       u32 tx_ko;
+       u32 data_rc;
+       u32 self_triggers;
+       u32 sw_retry_failure;
+       u32 illgl_rate_phy_err;
+       u32 pdev_cont_xretry;
+       u32 pdev_tx_timeout;
+       u32 pdev_resets;
+       u32 phy_underrun;
+       u32 txop_ovf;
+
+       /* PDEV RX stats */
+       s32 mid_ppdu_route_change;
+       s32 status_rcvd;
+       s32 r0_frags;
+       s32 r1_frags;
+       s32 r2_frags;
+       s32 r3_frags;
+       s32 htt_msdus;
+       s32 htt_mpdus;
+       s32 loc_msdus;
+       s32 loc_mpdus;
+       s32 oversize_amsdu;
+       s32 phy_errs;
+       s32 phy_err_drop;
+       s32 mpdu_errs;
+
+       /* VDEV STATS */
+
+       /* PEER STATS */
+       u8 peers;
+       struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
+
+       /* TODO: Beacon filter stats */
+
+};
+
+#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
+
+struct ath10k_peer {
+       struct list_head list;
+       int vdev_id;
+       u8 addr[ETH_ALEN];
+       DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+       struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
+};
+
+#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
+
+struct ath10k_vif {
+       u32 vdev_id;
+       enum wmi_vdev_type vdev_type;
+       enum wmi_vdev_subtype vdev_subtype;
+       u32 beacon_interval;
+       u32 dtim_period;
+
+       struct ath10k *ar;
+       struct ieee80211_vif *vif;
+
+       struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
+       u8 def_wep_key_index;
+
+       u16 tx_seq_no;
+
+       union {
+               struct {
+                       u8 bssid[ETH_ALEN];
+                       u32 uapsd;
+               } sta;
+               struct {
+                       /* 127 stations; wmi limit */
+                       u8 tim_bitmap[16];
+                       u8 tim_len;
+                       u32 ssid_len;
+                       u8 ssid[IEEE80211_MAX_SSID_LEN];
+                       bool hidden_ssid;
+                       /* P2P_IE with NoA attribute for P2P_GO case */
+                       u32 noa_len;
+                       u8 *noa_data;
+               } ap;
+               struct {
+                       u8 bssid[ETH_ALEN];
+               } ibss;
+       } u;
+};
+
+struct ath10k_vif_iter {
+       u32 vdev_id;
+       struct ath10k_vif *arvif;
+};
+
+struct ath10k_debug {
+       struct dentry *debugfs_phy;
+
+       struct ath10k_target_stats target_stats;
+       u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+
+       struct completion event_stats_compl;
+};
+
+struct ath10k {
+       struct ath_common ath_common;
+       struct ieee80211_hw *hw;
+       struct device *dev;
+       u8 mac_addr[ETH_ALEN];
+
+       u32 target_version;
+       u8 fw_version_major;
+       u32 fw_version_minor;
+       u16 fw_version_release;
+       u16 fw_version_build;
+       u32 phy_capability;
+       u32 hw_min_tx_power;
+       u32 hw_max_tx_power;
+       u32 ht_cap_info;
+       u32 vht_cap_info;
+
+       struct targetdef *targetdef;
+       struct hostdef *hostdef;
+
+       bool p2p;
+
+       struct {
+               void *priv;
+               enum ath10k_bus bus;
+               const struct ath10k_hif_ops *ops;
+       } hif;
+
+       struct ath10k_wmi wmi;
+
+       wait_queue_head_t event_queue;
+       bool is_target_paused;
+
+       struct ath10k_bmi bmi;
+
+       struct ath10k_htc *htc;
+       struct ath10k_htt *htt;
+
+       struct ath10k_hw_params {
+               u32 id;
+               const char *name;
+               u32 patch_load_addr;
+
+               struct ath10k_hw_params_fw {
+                       const char *dir;
+                       const char *fw;
+                       const char *otp;
+                       const char *board;
+               } fw;
+       } hw_params;
+
+       struct {
+               struct completion started;
+               struct completion completed;
+               struct completion on_channel;
+               struct timer_list timeout;
+               bool is_roc;
+               bool in_progress;
+               bool aborting;
+               int vdev_id;
+               int roc_freq;
+       } scan;
+
+       struct {
+               struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+       } mac;
+
+       /* should never be NULL; needed for regular htt rx */
+       struct ieee80211_channel *rx_channel;
+
+       /* valid during scan; needed for mgmt rx during scan */
+       struct ieee80211_channel *scan_channel;
+
+       int free_vdev_map;
+       int monitor_vdev_id;
+       bool monitor_enabled;
+       bool monitor_present;
+       unsigned int filter_flags;
+
+       struct wmi_pdev_set_wmm_params_arg wmm_params;
+       struct completion install_key_done;
+
+       struct completion vdev_setup_done;
+
+       struct workqueue_struct *workqueue;
+
+       /* prevents concurrent FW reconfiguration */
+       struct mutex conf_mutex;
+
+       /* protects shared structure data */
+       spinlock_t data_lock;
+
+       struct list_head peers;
+       wait_queue_head_t peer_mapping_wq;
+
+       struct work_struct offchan_tx_work;
+       struct sk_buff_head offchan_tx_queue;
+       struct completion offchan_tx_completed;
+       struct sk_buff *offchan_tx_skb;
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+       struct ath10k_debug debug;
+#endif
+};
+
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+                                 enum ath10k_bus bus,
+                                 const struct ath10k_hif_ops *hif_ops);
+void ath10k_core_destroy(struct ath10k *ar);
+
+int ath10k_core_register(struct ath10k *ar);
+void ath10k_core_unregister(struct ath10k *ar);
+
+int ath10k_core_target_suspend(struct ath10k *ar);
+int ath10k_core_target_resume(struct ath10k *ar);
+
+#endif /* _CORE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
new file mode 100644 (file)
index 0000000..499034b
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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/module.h>
+#include <linux/debugfs.h>
+
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_printk(const char *level, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       int rtn;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       rtn = printk("%sath10k: %pV", level, &vaf);
+
+       va_end(args);
+
+       return rtn;
+}
+
+int ath10k_info(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath10k_printk(KERN_INFO, "%pV", &vaf);
+       trace_ath10k_log_info(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_info);
+
+int ath10k_err(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+       ret = ath10k_printk(KERN_ERR, "%pV", &vaf);
+       trace_ath10k_log_err(&vaf);
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_err);
+
+int ath10k_warn(const char *fmt, ...)
+{
+       struct va_format vaf = {
+               .fmt = fmt,
+       };
+       va_list args;
+       int ret = 0;
+
+       va_start(args, fmt);
+       vaf.va = &args;
+
+       if (net_ratelimit())
+               ret = ath10k_printk(KERN_WARNING, "%pV", &vaf);
+
+       trace_ath10k_log_warn(&vaf);
+
+       va_end(args);
+
+       return ret;
+}
+EXPORT_SYMBOL(ath10k_warn);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+
+void ath10k_debug_read_service_map(struct ath10k *ar,
+                                  void *service_map,
+                                  size_t map_size)
+{
+       memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
+}
+
+static ssize_t ath10k_read_wmi_services(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char *buf;
+       unsigned int len = 0, buf_len = 1500;
+       const char *status;
+       ssize_t ret_cnt;
+       int i;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (len > buf_len)
+               len = buf_len;
+
+       for (i = 0; i < WMI_SERVICE_LAST; i++) {
+               if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i))
+                       status = "enabled";
+               else
+                       status = "disabled";
+
+               len += scnprintf(buf + len, buf_len - len,
+                                "0x%02x - %20s - %s\n",
+                                i, wmi_service_name(i), status);
+       }
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_wmi_services = {
+       .read = ath10k_read_wmi_services,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                   struct wmi_stats_event *ev)
+{
+       u8 *tmp = ev->data;
+       struct ath10k_target_stats *stats;
+       int num_pdev_stats, num_vdev_stats, num_peer_stats;
+       struct wmi_pdev_stats *ps;
+       int i;
+
+       mutex_lock(&ar->conf_mutex);
+
+       stats = &ar->debug.target_stats;
+
+       num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
+       num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
+       num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
+
+       if (num_pdev_stats) {
+               ps = (struct wmi_pdev_stats *)tmp;
+
+               stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
+               stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
+               stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
+               stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
+               stats->cycle_count = __le32_to_cpu(ps->cycle_count);
+               stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
+               stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
+
+               stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
+               stats->comp_delivered =
+                       __le32_to_cpu(ps->wal.tx.comp_delivered);
+               stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
+               stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
+               stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
+               stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
+               stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
+               stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
+               stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
+               stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
+               stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
+               stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
+               stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
+               stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
+               stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
+               stats->sw_retry_failure =
+                       __le32_to_cpu(ps->wal.tx.sw_retry_failure);
+               stats->illgl_rate_phy_err =
+                       __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
+               stats->pdev_cont_xretry =
+                       __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
+               stats->pdev_tx_timeout =
+                       __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
+               stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
+               stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
+               stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
+
+               stats->mid_ppdu_route_change =
+                       __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
+               stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
+               stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
+               stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
+               stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
+               stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
+               stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
+               stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
+               stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
+               stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
+               stats->oversize_amsdu =
+                       __le32_to_cpu(ps->wal.rx.oversize_amsdu);
+               stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
+               stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
+               stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
+
+               tmp += sizeof(struct wmi_pdev_stats);
+       }
+
+       /* 0 or max vdevs */
+       /* Currently firmware does not support VDEV stats */
+       if (num_vdev_stats) {
+               struct wmi_vdev_stats *vdev_stats;
+
+               for (i = 0; i < num_vdev_stats; i++) {
+                       vdev_stats = (struct wmi_vdev_stats *)tmp;
+                       tmp += sizeof(struct wmi_vdev_stats);
+               }
+       }
+
+       if (num_peer_stats) {
+               struct wmi_peer_stats *peer_stats;
+               struct ath10k_peer_stat *s;
+
+               stats->peers = num_peer_stats;
+
+               for (i = 0; i < num_peer_stats; i++) {
+                       peer_stats = (struct wmi_peer_stats *)tmp;
+                       s = &stats->peer_stat[i];
+
+                       WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
+                                                  s->peer_macaddr);
+                       s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
+                       s->peer_tx_rate =
+                               __le32_to_cpu(peer_stats->peer_tx_rate);
+
+                       tmp += sizeof(struct wmi_peer_stats);
+               }
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       complete(&ar->debug.event_stats_compl);
+}
+
+static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       struct ath10k_target_stats *fw_stats;
+       char *buf;
+       unsigned int len = 0, buf_len = 2500;
+       ssize_t ret_cnt;
+       long left;
+       int i;
+       int ret;
+
+       fw_stats = &ar->debug.target_stats;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+       if (ret) {
+               ath10k_warn("could not request stats (%d)\n", ret);
+               kfree(buf);
+               return -EIO;
+       }
+
+       left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+
+       if (left <= 0) {
+               kfree(buf);
+               return -ETIMEDOUT;
+       }
+
+       mutex_lock(&ar->conf_mutex);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Channel noise floor", fw_stats->ch_noise_floor);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "Channel TX power", fw_stats->chan_tx_power);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "TX frame count", fw_stats->tx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RX frame count", fw_stats->rx_frame_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RX clear count", fw_stats->rx_clear_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "Cycle count", fw_stats->cycle_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "PHY error count", fw_stats->phy_err_count);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV TX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies queued", fw_stats->comp_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HTT cookies disp.", fw_stats->comp_delivered);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDU queued", fw_stats->msdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU queued", fw_stats->mpdu_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs dropped", fw_stats->wmm_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local enqued", fw_stats->local_enqued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Local freed", fw_stats->local_freed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW queued", fw_stats->hw_queued);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs reaped", fw_stats->hw_reaped);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Num underruns", fw_stats->underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PPDUs cleaned", fw_stats->tx_abort);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs requed", fw_stats->mpdus_requed);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Excessive retries", fw_stats->tx_ko);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "HW rate", fw_stats->data_rc);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Sched self tiggers", fw_stats->self_triggers);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Dropped due to SW retries",
+                        fw_stats->sw_retry_failure);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Illegal rate phy errors",
+                        fw_stats->illgl_rate_phy_err);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "TX timeout", fw_stats->pdev_tx_timeout);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PDEV resets", fw_stats->pdev_resets);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY underrun", fw_stats->phy_underrun);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU is more than txop limit", fw_stats->txop_ovf);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PDEV RX stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Mid PPDU route change",
+                        fw_stats->mid_ppdu_route_change);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Tot. number of statuses", fw_stats->status_rcvd);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 0", fw_stats->r0_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 1", fw_stats->r1_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 2", fw_stats->r2_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Extra frags on rings 3", fw_stats->r3_frags);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to HTT", fw_stats->htt_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MSDUs delivered to stack", fw_stats->loc_msdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDUs delivered to stack", fw_stats->loc_mpdus);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "Oversized AMSUs", fw_stats->oversize_amsdu);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors", fw_stats->phy_errs);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "PHY errors drops", fw_stats->phy_err_drop);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+                        "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n",
+                        "ath10k PEER stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "=================");
+
+       for (i = 0; i < fw_stats->peers; i++) {
+               len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+                                "Peer MAC address",
+                                fw_stats->peer_stat[i].peer_macaddr);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer TX rate",
+                                fw_stats->peer_stat[i].peer_tx_rate);
+               len += scnprintf(buf + len, buf_len - len, "\n");
+       }
+
+       if (len > buf_len)
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       mutex_unlock(&ar->conf_mutex);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_fw_stats = {
+       .read = ath10k_read_fw_stats,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+int ath10k_debug_create(struct ath10k *ar)
+{
+       ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
+                                                  ar->hw->wiphy->debugfsdir);
+
+       if (!ar->debug.debugfs_phy)
+               return -ENOMEM;
+
+       init_completion(&ar->debug.event_stats_compl);
+
+       debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
+                           &fops_fw_stats);
+
+       debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
+                           &fops_wmi_services);
+
+       return 0;
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       if (ath10k_debug_mask & mask)
+               ath10k_printk(KERN_DEBUG, "%pV", &vaf);
+
+       trace_ath10k_log_dbg(mask, &vaf);
+
+       va_end(args);
+}
+EXPORT_SYMBOL(ath10k_dbg);
+
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                    const char *msg, const char *prefix,
+                    const void *buf, size_t len)
+{
+       if (ath10k_debug_mask & mask) {
+               if (msg)
+                       ath10k_dbg(mask, "%s\n", msg);
+
+               print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+       }
+
+       /* tracing code doesn't like null strings :/ */
+       trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+                                 buf, len);
+}
+EXPORT_SYMBOL(ath10k_dbg_dump);
+
+#endif /* CONFIG_ATH10K_DEBUG */
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
new file mode 100644 (file)
index 0000000..168140c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _DEBUG_H_
+#define _DEBUG_H_
+
+#include <linux/types.h>
+#include "trace.h"
+
+enum ath10k_debug_mask {
+       ATH10K_DBG_PCI          = 0x00000001,
+       ATH10K_DBG_WMI          = 0x00000002,
+       ATH10K_DBG_HTC          = 0x00000004,
+       ATH10K_DBG_HTT          = 0x00000008,
+       ATH10K_DBG_MAC          = 0x00000010,
+       ATH10K_DBG_CORE         = 0x00000020,
+       ATH10K_DBG_PCI_DUMP     = 0x00000040,
+       ATH10K_DBG_HTT_DUMP     = 0x00000080,
+       ATH10K_DBG_MGMT         = 0x00000100,
+       ATH10K_DBG_DATA         = 0x00000200,
+       ATH10K_DBG_ANY          = 0xffffffff,
+};
+
+extern unsigned int ath10k_debug_mask;
+
+extern __printf(1, 2) int ath10k_info(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_read_service_map(struct ath10k *ar,
+                                  void *service_map,
+                                  size_t map_size);
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                   struct wmi_stats_event *ev);
+
+#else
+static inline int ath10k_debug_create(struct ath10k *ar)
+{
+       return 0;
+}
+
+static inline void ath10k_debug_read_service_map(struct ath10k *ar,
+                                                void *service_map,
+                                                size_t map_size)
+{
+}
+
+static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
+                                                 struct wmi_stats_event *ev)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
+                                     const char *fmt, ...);
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                    const char *msg, const char *prefix,
+                    const void *buf, size_t len);
+#else /* CONFIG_ATH10K_DEBUG */
+
+static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask,
+                            const char *fmt, ...)
+{
+       return 0;
+}
+
+static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+                                  const char *msg, const char *prefix,
+                                  const void *buf, size_t len)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUG */
+#endif /* _DEBUG_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
new file mode 100644 (file)
index 0000000..73a24d4
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _HIF_H_
+#define _HIF_H_
+
+#include <linux/kernel.h>
+#include "core.h"
+
+struct ath10k_hif_cb {
+       int (*tx_completion)(struct ath10k *ar,
+                            struct sk_buff *wbuf,
+                            unsigned transfer_id);
+       int (*rx_completion)(struct ath10k *ar,
+                            struct sk_buff *wbuf,
+                            u8 pipe_id);
+};
+
+struct ath10k_hif_ops {
+       /* Send the head of a buffer to HIF for transmission to the target. */
+       int (*send_head)(struct ath10k *ar, u8 pipe_id,
+                        unsigned int transfer_id,
+                        unsigned int nbytes,
+                        struct sk_buff *buf);
+
+       /*
+        * API to handle HIF-specific BMI message exchanges, this API is
+        * synchronous and only allowed to be called from a context that
+        * can block (sleep)
+        */
+       int (*exchange_bmi_msg)(struct ath10k *ar,
+                               void *request, u32 request_len,
+                               void *response, u32 *response_len);
+
+       int (*start)(struct ath10k *ar);
+
+       void (*stop)(struct ath10k *ar);
+
+       int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
+                                  u8 *ul_pipe, u8 *dl_pipe,
+                                  int *ul_is_polled, int *dl_is_polled);
+
+       void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
+
+       /*
+        * Check if prior sends have completed.
+        *
+        * Check whether the pipe in question has any completed
+        * sends that have not yet been processed.
+        * This function is only relevant for HIF pipes that are configured
+        * to be polled rather than interrupt-driven.
+        */
+       void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
+
+       void (*init)(struct ath10k *ar,
+                    struct ath10k_hif_cb *callbacks);
+
+       u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+};
+
+
+static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id,
+                                      unsigned int transfer_id,
+                                      unsigned int nbytes,
+                                      struct sk_buff *buf)
+{
+       return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf);
+}
+
+static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
+                                             void *request, u32 request_len,
+                                             void *response, u32 *response_len)
+{
+       return ar->hif.ops->exchange_bmi_msg(ar, request, request_len,
+                                            response, response_len);
+}
+
+static inline int ath10k_hif_start(struct ath10k *ar)
+{
+       return ar->hif.ops->start(ar);
+}
+
+static inline void ath10k_hif_stop(struct ath10k *ar)
+{
+       return ar->hif.ops->stop(ar);
+}
+
+static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
+                                                u16 service_id,
+                                                u8 *ul_pipe, u8 *dl_pipe,
+                                                int *ul_is_polled,
+                                                int *dl_is_polled)
+{
+       return ar->hif.ops->map_service_to_pipe(ar, service_id,
+                                               ul_pipe, dl_pipe,
+                                               ul_is_polled, dl_is_polled);
+}
+
+static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
+                                              u8 *ul_pipe, u8 *dl_pipe)
+{
+       ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe);
+}
+
+static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
+                                                 u8 pipe_id, int force)
+{
+       ar->hif.ops->send_complete_check(ar, pipe_id, force);
+}
+
+static inline void ath10k_hif_init(struct ath10k *ar,
+                                  struct ath10k_hif_cb *callbacks)
+{
+       ar->hif.ops->init(ar, callbacks);
+}
+
+static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
+                                                  u8 pipe_id)
+{
+       return ar->hif.ops->get_free_queue_number(ar, pipe_id);
+}
+
+#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
new file mode 100644 (file)
index 0000000..74363c9
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 "core.h"
+#include "hif.h"
+#include "debug.h"
+
+/********/
+/* Send */
+/********/
+
+static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep,
+                                                 int force)
+{
+       /*
+        * Check whether HIF has any prior sends that have finished,
+        * have not had the post-processing done.
+        */
+       ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force);
+}
+
+static void ath10k_htc_control_tx_complete(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       kfree_skb(skb);
+}
+
+static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+
+       skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
+       if (!skb) {
+               ath10k_warn("Unable to allocate ctrl skb\n");
+               return NULL;
+       }
+
+       skb_reserve(skb, 20); /* FIXME: why 20 bytes? */
+       WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+       skb_cb = ATH10K_SKB_CB(skb);
+       memset(skb_cb, 0, sizeof(*skb_cb));
+
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
+       return skb;
+}
+
+static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
+                                            struct sk_buff *skb)
+{
+       ath10k_skb_unmap(htc->ar->dev, skb);
+       skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+}
+
+static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+                  ep->eid, skb);
+
+       ath10k_htc_restore_tx_skb(ep->htc, skb);
+
+       if (!ep->ep_ops.ep_tx_complete) {
+               ath10k_warn("no tx handler for eid %d\n", ep->eid);
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       ep->ep_ops.ep_tx_complete(ep->htc->ar, skb);
+}
+
+/* assumes tx_lock is held */
+static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep)
+{
+       if (!ep->tx_credit_flow_enabled)
+               return false;
+       if (ep->tx_credits >= ep->tx_credits_per_max_message)
+               return false;
+
+       ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n",
+                  ep->eid);
+       return true;
+}
+
+static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
+                                     struct sk_buff *skb)
+{
+       struct ath10k_htc_hdr *hdr;
+
+       hdr = (struct ath10k_htc_hdr *)skb->data;
+       memset(hdr, 0, sizeof(*hdr));
+
+       hdr->eid = ep->eid;
+       hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
+
+       spin_lock_bh(&ep->htc->tx_lock);
+       hdr->seq_no = ep->seq_no++;
+
+       if (ath10k_htc_ep_need_credit_update(ep))
+               hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE;
+
+       spin_unlock_bh(&ep->htc->tx_lock);
+}
+
+static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
+                               struct ath10k_htc_ep *ep,
+                               struct sk_buff *skb,
+                               u8 credits)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+                  ep->eid, skb);
+
+       ath10k_htc_prepare_tx_skb(ep, skb);
+
+       ret = ath10k_skb_map(htc->ar->dev, skb);
+       if (ret)
+               goto err;
+
+       ret = ath10k_hif_send_head(htc->ar,
+                                  ep->ul_pipe_id,
+                                  ep->eid,
+                                  skb->len,
+                                  skb);
+       if (unlikely(ret))
+               goto err;
+
+       return 0;
+err:
+       ath10k_warn("HTC issue failed: %d\n", ret);
+
+       spin_lock_bh(&htc->tx_lock);
+       ep->tx_credits += credits;
+       spin_unlock_bh(&htc->tx_lock);
+
+       /* this is the simplest way to handle out-of-resources for non-credit
+        * based endpoints. credit based endpoints can still get -ENOSR, but
+        * this is highly unlikely as credit reservation should prevent that */
+       if (ret == -ENOSR) {
+               spin_lock_bh(&htc->tx_lock);
+               __skb_queue_head(&ep->tx_queue, skb);
+               spin_unlock_bh(&htc->tx_lock);
+
+               return ret;
+       }
+
+       skb_cb->is_aborted = true;
+       ath10k_htc_notify_tx_completion(ep, skb);
+
+       return ret;
+}
+
+static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
+                                                      struct ath10k_htc_ep *ep,
+                                                      u8 *credits)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+       int credits_required;
+       int remainder;
+       unsigned int transfer_len;
+
+       lockdep_assert_held(&htc->tx_lock);
+
+       skb = __skb_dequeue(&ep->tx_queue);
+       if (!skb)
+               return NULL;
+
+       skb_cb = ATH10K_SKB_CB(skb);
+       transfer_len = skb->len;
+
+       if (likely(transfer_len <= htc->target_credit_size)) {
+               credits_required = 1;
+       } else {
+               /* figure out how many credits this message requires */
+               credits_required = transfer_len / htc->target_credit_size;
+               remainder = transfer_len % htc->target_credit_size;
+
+               if (remainder)
+                       credits_required++;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
+                  credits_required, ep->tx_credits);
+
+       if (ep->tx_credits < credits_required) {
+               __skb_queue_head(&ep->tx_queue, skb);
+               return NULL;
+       }
+
+       ep->tx_credits -= credits_required;
+       *credits = credits_required;
+       return skb;
+}
+
+static void ath10k_htc_send_work(struct work_struct *work)
+{
+       struct ath10k_htc_ep *ep = container_of(work,
+                                       struct ath10k_htc_ep, send_work);
+       struct ath10k_htc *htc = ep->htc;
+       struct sk_buff *skb;
+       u8 credits = 0;
+       int ret;
+
+       while (true) {
+               if (ep->ul_is_polled)
+                       ath10k_htc_send_complete_check(ep, 0);
+
+               spin_lock_bh(&htc->tx_lock);
+               if (ep->tx_credit_flow_enabled)
+                       skb = ath10k_htc_get_skb_credit_based(htc, ep,
+                                                             &credits);
+               else
+                       skb = __skb_dequeue(&ep->tx_queue);
+               spin_unlock_bh(&htc->tx_lock);
+
+               if (!skb)
+                       break;
+
+               ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
+               if (ret == -ENOSR)
+                       break;
+       }
+}
+
+int ath10k_htc_send(struct ath10k_htc *htc,
+                   enum ath10k_htc_ep_id eid,
+                   struct sk_buff *skb)
+{
+       struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+
+       if (eid >= ATH10K_HTC_EP_COUNT) {
+               ath10k_warn("Invalid endpoint id: %d\n", eid);
+               return -ENOENT;
+       }
+
+       skb_push(skb, sizeof(struct ath10k_htc_hdr));
+
+       spin_lock_bh(&htc->tx_lock);
+       __skb_queue_tail(&ep->tx_queue, skb);
+       spin_unlock_bh(&htc->tx_lock);
+
+       queue_work(htc->ar->workqueue, &ep->send_work);
+       return 0;
+}
+
+static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           unsigned int eid)
+{
+       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+       bool stopping;
+
+       ath10k_htc_notify_tx_completion(ep, skb);
+       /* the skb now belongs to the completion handler */
+
+       spin_lock_bh(&htc->tx_lock);
+       stopping = htc->stopping;
+       spin_unlock_bh(&htc->tx_lock);
+
+       if (!ep->tx_credit_flow_enabled && !stopping)
+               /*
+                * note: when using TX credit flow, the re-checking of
+                * queues happens when credits flow back from the target.
+                * in the non-TX credit case, we recheck after the packet
+                * completes
+                */
+               queue_work(ar->workqueue, &ep->send_work);
+
+       return 0;
+}
+
+/* flush endpoint TX queue */
+static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
+                                        struct ath10k_htc_ep *ep)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *skb_cb;
+
+       spin_lock_bh(&htc->tx_lock);
+       for (;;) {
+               skb = __skb_dequeue(&ep->tx_queue);
+               if (!skb)
+                       break;
+
+               skb_cb = ATH10K_SKB_CB(skb);
+               skb_cb->is_aborted = true;
+               ath10k_htc_notify_tx_completion(ep, skb);
+       }
+       spin_unlock_bh(&htc->tx_lock);
+
+       cancel_work_sync(&ep->send_work);
+}
+
+/***********/
+/* Receive */
+/***********/
+
+static void
+ath10k_htc_process_credit_report(struct ath10k_htc *htc,
+                                const struct ath10k_htc_credit_report *report,
+                                int len,
+                                enum ath10k_htc_ep_id eid)
+{
+       struct ath10k_htc_ep *ep;
+       int i, n_reports;
+
+       if (len % sizeof(*report))
+               ath10k_warn("Uneven credit report len %d", len);
+
+       n_reports = len / sizeof(*report);
+
+       spin_lock_bh(&htc->tx_lock);
+       for (i = 0; i < n_reports; i++, report++) {
+               if (report->eid >= ATH10K_HTC_EP_COUNT)
+                       break;
+
+               ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n",
+                          report->eid, report->credits);
+
+               ep = &htc->endpoint[report->eid];
+               ep->tx_credits += report->credits;
+
+               if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
+                       queue_work(htc->ar->workqueue, &ep->send_work);
+       }
+       spin_unlock_bh(&htc->tx_lock);
+}
+
+static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
+                                     u8 *buffer,
+                                     int length,
+                                     enum ath10k_htc_ep_id src_eid)
+{
+       int status = 0;
+       struct ath10k_htc_record *record;
+       u8 *orig_buffer;
+       int orig_length;
+       size_t len;
+
+       orig_buffer = buffer;
+       orig_length = length;
+
+       while (length > 0) {
+               record = (struct ath10k_htc_record *)buffer;
+
+               if (length < sizeof(record->hdr)) {
+                       status = -EINVAL;
+                       break;
+               }
+
+               if (record->hdr.len > length) {
+                       /* no room left in buffer for record */
+                       ath10k_warn("Invalid record length: %d\n",
+                                   record->hdr.len);
+                       status = -EINVAL;
+                       break;
+               }
+
+               switch (record->hdr.id) {
+               case ATH10K_HTC_RECORD_CREDITS:
+                       len = sizeof(struct ath10k_htc_credit_report);
+                       if (record->hdr.len < len) {
+                               ath10k_warn("Credit report too long\n");
+                               status = -EINVAL;
+                               break;
+                       }
+                       ath10k_htc_process_credit_report(htc,
+                                                        record->credit_report,
+                                                        record->hdr.len,
+                                                        src_eid);
+                       break;
+               default:
+                       ath10k_warn("Unhandled record: id:%d length:%d\n",
+                                   record->hdr.id, record->hdr.len);
+                       break;
+               }
+
+               if (status)
+                       break;
+
+               /* multiple records may be present in a trailer */
+               buffer += sizeof(record->hdr) + record->hdr.len;
+               length -= sizeof(record->hdr) + record->hdr.len;
+       }
+
+       if (status)
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "",
+                               orig_buffer, orig_length);
+
+       return status;
+}
+
+static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
+                                           struct sk_buff *skb,
+                                           u8 pipe_id)
+{
+       int status = 0;
+       struct ath10k_htc *htc = ar->htc;
+       struct ath10k_htc_hdr *hdr;
+       struct ath10k_htc_ep *ep;
+       u16 payload_len;
+       u32 trailer_len = 0;
+       size_t min_len;
+       u8 eid;
+       bool trailer_present;
+
+       hdr = (struct ath10k_htc_hdr *)skb->data;
+       skb_pull(skb, sizeof(*hdr));
+
+       eid = hdr->eid;
+
+       if (eid >= ATH10K_HTC_EP_COUNT) {
+               ath10k_warn("HTC Rx: invalid eid %d\n", eid);
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "",
+                               hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       ep = &htc->endpoint[eid];
+
+       /*
+        * If this endpoint that received a message from the target has
+        * a to-target HIF pipe whose send completions are polled rather
+        * than interrupt-driven, this is a good point to ask HIF to check
+        * whether it has any completed sends to handle.
+        */
+       if (ep->ul_is_polled)
+               ath10k_htc_send_complete_check(ep, 1);
+
+       payload_len = __le16_to_cpu(hdr->len);
+
+       if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
+               ath10k_warn("HTC rx frame too long, len: %zu\n",
+                           payload_len + sizeof(*hdr));
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "",
+                               hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       if (skb->len < payload_len) {
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC Rx: insufficient length, got %d, expected %d\n",
+                          skb->len, payload_len);
+               ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len",
+                               "", hdr, sizeof(*hdr));
+               status = -EINVAL;
+               goto out;
+       }
+
+       /* get flags to check for trailer */
+       trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
+       if (trailer_present) {
+               u8 *trailer;
+
+               trailer_len = hdr->trailer_len;
+               min_len = sizeof(struct ath10k_ath10k_htc_record_hdr);
+
+               if ((trailer_len < min_len) ||
+                   (trailer_len > payload_len)) {
+                       ath10k_warn("Invalid trailer length: %d\n",
+                                   trailer_len);
+                       status = -EPROTO;
+                       goto out;
+               }
+
+               trailer = (u8 *)hdr;
+               trailer += sizeof(*hdr);
+               trailer += payload_len;
+               trailer -= trailer_len;
+               status = ath10k_htc_process_trailer(htc, trailer,
+                                                   trailer_len, hdr->eid);
+               if (status)
+                       goto out;
+
+               skb_trim(skb, skb->len - trailer_len);
+       }
+
+       if (((int)payload_len - (int)trailer_len) <= 0)
+               /* zero length packet with trailer data, just drop these */
+               goto out;
+
+       if (eid == ATH10K_HTC_EP_0) {
+               struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data;
+
+               switch (__le16_to_cpu(msg->hdr.message_id)) {
+               default:
+                       /* handle HTC control message */
+                       if (completion_done(&htc->ctl_resp)) {
+                               /*
+                                * this is a fatal error, target should not be
+                                * sending unsolicited messages on the ep 0
+                                */
+                               ath10k_warn("HTC rx ctrl still processing\n");
+                               status = -EINVAL;
+                               complete(&htc->ctl_resp);
+                               goto out;
+                       }
+
+                       htc->control_resp_len =
+                               min_t(int, skb->len,
+                                     ATH10K_HTC_MAX_CTRL_MSG_LEN);
+
+                       memcpy(htc->control_resp_buffer, skb->data,
+                              htc->control_resp_len);
+
+                       complete(&htc->ctl_resp);
+                       break;
+               case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE:
+                       htc->htc_ops.target_send_suspend_complete(ar);
+               }
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
+                  eid, skb);
+       ep->ep_ops.ep_rx_complete(ar, skb);
+
+       /* skb is now owned by the rx completion handler */
+       skb = NULL;
+out:
+       kfree_skb(skb);
+
+       return status;
+}
+
+static void ath10k_htc_control_rx_complete(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       /* This is unexpected. FW is not supposed to send regular rx on this
+        * endpoint. */
+       ath10k_warn("unexpected htc rx\n");
+       kfree_skb(skb);
+}
+
+/***************/
+/* Init/Deinit */
+/***************/
+
+static const char *htc_service_name(enum ath10k_htc_svc_id id)
+{
+       switch (id) {
+       case ATH10K_HTC_SVC_ID_RESERVED:
+               return "Reserved";
+       case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+               return "Control";
+       case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+               return "WMI";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+               return "DATA BE";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+               return "DATA BK";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+               return "DATA VI";
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+               return "DATA VO";
+       case ATH10K_HTC_SVC_ID_NMI_CONTROL:
+               return "NMI Control";
+       case ATH10K_HTC_SVC_ID_NMI_DATA:
+               return "NMI Data";
+       case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+               return "HTT Data";
+       case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+               return "RAW";
+       }
+
+       return "Unknown";
+}
+
+static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
+{
+       struct ath10k_htc_ep *ep;
+       int i;
+
+       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+               ep = &htc->endpoint[i];
+               ep->service_id = ATH10K_HTC_SVC_ID_UNUSED;
+               ep->max_ep_message_len = 0;
+               ep->max_tx_queue_depth = 0;
+               ep->eid = i;
+               skb_queue_head_init(&ep->tx_queue);
+               ep->htc = htc;
+               ep->tx_credit_flow_enabled = true;
+               INIT_WORK(&ep->send_work, ath10k_htc_send_work);
+       }
+}
+
+static void ath10k_htc_setup_target_buffer_assignments(struct ath10k_htc *htc)
+{
+       struct ath10k_htc_svc_tx_credits *entry;
+
+       entry = &htc->service_tx_alloc[0];
+
+       /*
+        * for PCIE allocate all credists/HTC buffers to WMI.
+        * no buffers are used/required for data. data always
+        * remains on host.
+        */
+       entry++;
+       entry->service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
+       entry->credit_allocation = htc->total_transmit_credits;
+}
+
+static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc,
+                                          u16 service_id)
+{
+       u8 allocation = 0;
+       int i;
+
+       for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
+               if (htc->service_tx_alloc[i].service_id == service_id)
+                       allocation =
+                           htc->service_tx_alloc[i].credit_allocation;
+       }
+
+       return allocation;
+}
+
+int ath10k_htc_wait_target(struct ath10k_htc *htc)
+{
+       int status = 0;
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+       struct ath10k_htc_msg *msg;
+       u16 message_id;
+       u16 credit_count;
+       u16 credit_size;
+
+       INIT_COMPLETION(htc->ctl_resp);
+
+       status = ath10k_hif_start(htc->ar);
+       if (status) {
+               ath10k_err("could not start HIF (%d)\n", status);
+               goto err_start;
+       }
+
+       status = wait_for_completion_timeout(&htc->ctl_resp,
+                                            ATH10K_HTC_WAIT_TIMEOUT_HZ);
+       if (status <= 0) {
+               if (status == 0)
+                       status = -ETIMEDOUT;
+
+               ath10k_err("ctl_resp never came in (%d)\n", status);
+               goto err_target;
+       }
+
+       if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
+               ath10k_err("Invalid HTC ready msg len:%d\n",
+                          htc->control_resp_len);
+
+               status = -ECOMM;
+               goto err_target;
+       }
+
+       msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+       message_id   = __le16_to_cpu(msg->hdr.message_id);
+       credit_count = __le16_to_cpu(msg->ready.credit_count);
+       credit_size  = __le16_to_cpu(msg->ready.credit_size);
+
+       if (message_id != ATH10K_HTC_MSG_READY_ID) {
+               ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id);
+               status = -ECOMM;
+               goto err_target;
+       }
+
+       htc->total_transmit_credits = credit_count;
+       htc->target_credit_size = credit_size;
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "Target ready! transmit resources: %d size:%d\n",
+                  htc->total_transmit_credits,
+                  htc->target_credit_size);
+
+       if ((htc->total_transmit_credits == 0) ||
+           (htc->target_credit_size == 0)) {
+               status = -ECOMM;
+               ath10k_err("Invalid credit size received\n");
+               goto err_target;
+       }
+
+       ath10k_htc_setup_target_buffer_assignments(htc);
+
+       /* setup our pseudo HTC control endpoint connection */
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+       conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete;
+       conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS;
+       conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL;
+
+       /* connect fake service */
+       status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
+       if (status) {
+               ath10k_err("could not connect to htc service (%d)\n", status);
+               goto err_target;
+       }
+
+       return 0;
+err_target:
+       ath10k_hif_stop(htc->ar);
+err_start:
+       return status;
+}
+
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+                              struct ath10k_htc_svc_conn_req *conn_req,
+                              struct ath10k_htc_svc_conn_resp *conn_resp)
+{
+       struct ath10k_htc_msg *msg;
+       struct ath10k_htc_conn_svc *req_msg;
+       struct ath10k_htc_conn_svc_response resp_msg_dummy;
+       struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy;
+       enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT;
+       struct ath10k_htc_ep *ep;
+       struct sk_buff *skb;
+       unsigned int max_msg_size = 0;
+       int length, status;
+       bool disable_credit_flow_ctrl = false;
+       u16 message_id, service_id, flags = 0;
+       u8 tx_alloc = 0;
+
+       /* special case for HTC pseudo control service */
+       if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) {
+               disable_credit_flow_ctrl = true;
+               assigned_eid = ATH10K_HTC_EP_0;
+               max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN;
+               memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
+               goto setup;
+       }
+
+       tx_alloc = ath10k_htc_get_credit_allocation(htc,
+                                                   conn_req->service_id);
+       if (!tx_alloc)
+               ath10k_warn("HTC Service %s does not allocate target credits\n",
+                           htc_service_name(conn_req->service_id));
+
+       skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+       if (!skb) {
+               ath10k_err("Failed to allocate HTC packet\n");
+               return -ENOMEM;
+       }
+
+       length = sizeof(msg->hdr) + sizeof(msg->connect_service);
+       skb_put(skb, length);
+       memset(skb->data, 0, length);
+
+       msg = (struct ath10k_htc_msg *)skb->data;
+       msg->hdr.message_id =
+               __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID);
+
+       flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
+
+       req_msg = &msg->connect_service;
+       req_msg->flags = __cpu_to_le16(flags);
+       req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
+       /* Only enable credit flow control for WMI ctrl service */
+       if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
+               flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
+               disable_credit_flow_ctrl = true;
+       }
+
+       INIT_COMPLETION(htc->ctl_resp);
+
+       status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+       if (status) {
+               kfree_skb(skb);
+               return status;
+       }
+
+       /* wait for response */
+       status = wait_for_completion_timeout(&htc->ctl_resp,
+                                            ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
+       if (status <= 0) {
+               if (status == 0)
+                       status = -ETIMEDOUT;
+               ath10k_err("Service connect timeout: %d\n", status);
+               return status;
+       }
+
+       /* we controlled the buffer creation, it's aligned */
+       msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+       resp_msg = &msg->connect_service_response;
+       message_id = __le16_to_cpu(msg->hdr.message_id);
+       service_id = __le16_to_cpu(resp_msg->service_id);
+
+       if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
+           (htc->control_resp_len < sizeof(msg->hdr) +
+            sizeof(msg->connect_service_response))) {
+               ath10k_err("Invalid resp message ID 0x%x", message_id);
+               return -EPROTO;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n",
+                  htc_service_name(service_id),
+                  resp_msg->status, resp_msg->eid);
+
+       conn_resp->connect_resp_code = resp_msg->status;
+
+       /* check response status */
+       if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) {
+               ath10k_err("HTC Service %s connect request failed: 0x%x)\n",
+                          htc_service_name(service_id),
+                          resp_msg->status);
+               return -EPROTO;
+       }
+
+       assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid;
+       max_msg_size = __le16_to_cpu(resp_msg->max_msg_size);
+
+setup:
+
+       if (assigned_eid >= ATH10K_HTC_EP_COUNT)
+               return -EPROTO;
+
+       if (max_msg_size == 0)
+               return -EPROTO;
+
+       ep = &htc->endpoint[assigned_eid];
+       ep->eid = assigned_eid;
+
+       if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED)
+               return -EPROTO;
+
+       /* return assigned endpoint to caller */
+       conn_resp->eid = assigned_eid;
+       conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size);
+
+       /* setup the endpoint */
+       ep->service_id = conn_req->service_id;
+       ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
+       ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size);
+       ep->tx_credits = tx_alloc;
+       ep->tx_credit_size = htc->target_credit_size;
+       ep->tx_credits_per_max_message = ep->max_ep_message_len /
+                                        htc->target_credit_size;
+
+       if (ep->max_ep_message_len % htc->target_credit_size)
+               ep->tx_credits_per_max_message++;
+
+       /* copy all the callbacks */
+       ep->ep_ops = conn_req->ep_ops;
+
+       status = ath10k_hif_map_service_to_pipe(htc->ar,
+                                               ep->service_id,
+                                               &ep->ul_pipe_id,
+                                               &ep->dl_pipe_id,
+                                               &ep->ul_is_polled,
+                                               &ep->dl_is_polled);
+       if (status)
+               return status;
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+                  htc_service_name(ep->service_id), ep->ul_pipe_id,
+                  ep->dl_pipe_id, ep->eid);
+
+       ath10k_dbg(ATH10K_DBG_HTC,
+                  "EP %d UL polled: %d, DL polled: %d\n",
+                  ep->eid, ep->ul_is_polled, ep->dl_is_polled);
+
+       if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
+               ep->tx_credit_flow_enabled = false;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "HTC service: %s eid: %d TX flow control disabled\n",
+                          htc_service_name(ep->service_id), assigned_eid);
+       }
+
+       return status;
+}
+
+struct sk_buff *ath10k_htc_alloc_skb(int size)
+{
+       struct sk_buff *skb;
+
+       skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
+       if (!skb) {
+               ath10k_warn("could not allocate HTC tx skb\n");
+               return NULL;
+       }
+
+       skb_reserve(skb, sizeof(struct ath10k_htc_hdr));
+
+       /* FW/HTC requires 4-byte aligned streams */
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("Unaligned HTC tx skb\n");
+
+       return skb;
+}
+
+int ath10k_htc_start(struct ath10k_htc *htc)
+{
+       struct sk_buff *skb;
+       int status = 0;
+       struct ath10k_htc_msg *msg;
+
+       skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext));
+       memset(skb->data, 0, skb->len);
+
+       msg = (struct ath10k_htc_msg *)skb->data;
+       msg->hdr.message_id =
+               __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+       ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
+
+       status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+       if (status) {
+               kfree_skb(skb);
+               return status;
+       }
+
+       return 0;
+}
+
+/*
+ * stop HTC communications, i.e. stop interrupt reception, and flush all
+ * queued buffers
+ */
+void ath10k_htc_stop(struct ath10k_htc *htc)
+{
+       int i;
+       struct ath10k_htc_ep *ep;
+
+       spin_lock_bh(&htc->tx_lock);
+       htc->stopping = true;
+       spin_unlock_bh(&htc->tx_lock);
+
+       for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+               ep = &htc->endpoint[i];
+               ath10k_htc_flush_endpoint_tx(htc, ep);
+       }
+
+       ath10k_hif_stop(htc->ar);
+       ath10k_htc_reset_endpoint_states(htc);
+}
+
+/* registered target arrival callback from the HIF layer */
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+                                    struct ath10k_htc_ops *htc_ops)
+{
+       struct ath10k_hif_cb htc_callbacks;
+       struct ath10k_htc_ep *ep = NULL;
+       struct ath10k_htc *htc = NULL;
+
+       /* FIXME: use struct ath10k instead */
+       htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
+       if (!htc)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_init(&htc->tx_lock);
+
+       memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
+
+       ath10k_htc_reset_endpoint_states(htc);
+
+       /* setup HIF layer callbacks */
+       htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler;
+       htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler;
+       htc->ar = ar;
+
+       /* Get HIF default pipe for HTC message exchange */
+       ep = &htc->endpoint[ATH10K_HTC_EP_0];
+
+       ath10k_hif_init(ar, &htc_callbacks);
+       ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
+
+       init_completion(&htc->ctl_resp);
+
+       return htc;
+}
+
+void ath10k_htc_destroy(struct ath10k_htc *htc)
+{
+       kfree(htc);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
new file mode 100644 (file)
index 0000000..fa45844
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _HTC_H_
+#define _HTC_H_
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/skbuff.h>
+#include <linux/semaphore.h>
+#include <linux/timer.h>
+
+struct ath10k;
+
+/****************/
+/* HTC protocol */
+/****************/
+
+/*
+ * HTC - host-target control protocol
+ *
+ * tx packets are generally <htc_hdr><payload>
+ * rx packets are more complex: <htc_hdr><payload><trailer>
+ *
+ * The payload + trailer length is stored in len.
+ * To get payload-only length one needs to payload - trailer_len.
+ *
+ * Trailer contains (possibly) multiple <htc_record>.
+ * Each record is a id-len-value.
+ *
+ * HTC header flags, control_byte0, control_byte1
+ * have different meaning depending whether its tx
+ * or rx.
+ *
+ * Alignment: htc_hdr, payload and trailer are
+ * 4-byte aligned.
+ */
+
+enum ath10k_htc_tx_flags {
+       ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
+       ATH10K_HTC_FLAG_SEND_BUNDLE        = 0x02
+};
+
+enum ath10k_htc_rx_flags {
+       ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02,
+       ATH10K_HTC_FLAG_BUNDLE_MASK     = 0xF0
+};
+
+struct ath10k_htc_hdr {
+       u8 eid; /* @enum ath10k_htc_ep_id */
+       u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */
+       __le16 len;
+       union {
+               u8 trailer_len; /* for rx */
+               u8 control_byte0;
+       } __packed;
+       union {
+               u8 seq_no; /* for tx */
+               u8 control_byte1;
+       } __packed;
+       u8 pad0;
+       u8 pad1;
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_msg_id {
+       ATH10K_HTC_MSG_READY_ID                = 1,
+       ATH10K_HTC_MSG_CONNECT_SERVICE_ID      = 2,
+       ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
+       ATH10K_HTC_MSG_SETUP_COMPLETE_ID       = 4,
+       ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID    = 5,
+       ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE   = 6
+};
+
+enum ath10k_htc_version {
+       ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
+       ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
+};
+
+enum ath10k_htc_conn_flags {
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH    = 0x0,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF      = 0x1,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2,
+       ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY         = 0x3,
+#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3
+       ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE    = 1 << 2,
+       ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB  8
+};
+
+enum ath10k_htc_conn_svc_status {
+       ATH10K_HTC_CONN_SVC_STATUS_SUCCESS      = 0,
+       ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND    = 1,
+       ATH10K_HTC_CONN_SVC_STATUS_FAILED       = 2,
+       ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
+       ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP   = 4
+};
+
+struct ath10k_ath10k_htc_msg_hdr {
+       __le16 message_id; /* @enum htc_message_id */
+} __packed;
+
+struct ath10k_htc_unknown {
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_ready {
+       __le16 credit_count;
+       __le16 credit_size;
+       u8 max_endpoints;
+       u8 pad0;
+} __packed;
+
+struct ath10k_htc_ready_extended {
+       struct ath10k_htc_ready base;
+       u8 htc_version; /* @enum ath10k_htc_version */
+       u8 max_msgs_per_htc_bundle;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc {
+       __le16 service_id;
+       __le16 flags; /* @enum ath10k_htc_conn_flags */
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc_response {
+       __le16 service_id;
+       u8 status; /* @enum ath10k_htc_conn_svc_status */
+       u8 eid;
+       __le16 max_msg_size;
+} __packed;
+
+struct ath10k_htc_setup_complete_extended {
+       u8 pad0;
+       u8 pad1;
+       __le32 flags; /* @enum htc_setup_complete_flags */
+       u8 max_msgs_per_bundled_recv;
+       u8 pad2;
+       u8 pad3;
+       u8 pad4;
+} __packed;
+
+struct ath10k_htc_msg {
+       struct ath10k_ath10k_htc_msg_hdr hdr;
+       union {
+               /* host-to-target */
+               struct ath10k_htc_conn_svc connect_service;
+               struct ath10k_htc_ready ready;
+               struct ath10k_htc_ready_extended ready_ext;
+               struct ath10k_htc_unknown unknown;
+               struct ath10k_htc_setup_complete_extended setup_complete_ext;
+
+               /* target-to-host */
+               struct ath10k_htc_conn_svc_response connect_service_response;
+       };
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_record_id {
+       ATH10K_HTC_RECORD_NULL    = 0,
+       ATH10K_HTC_RECORD_CREDITS = 1
+};
+
+struct ath10k_ath10k_htc_record_hdr {
+       u8 id; /* @enum ath10k_ath10k_htc_record_id */
+       u8 len;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_credit_report {
+       u8 eid; /* @enum ath10k_htc_ep_id */
+       u8 credits;
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct ath10k_htc_record {
+       struct ath10k_ath10k_htc_record_hdr hdr;
+       union {
+               struct ath10k_htc_credit_report credit_report[0];
+               u8 pauload[0];
+       };
+} __packed __aligned(4);
+
+/*
+ * note: the trailer offset is dynamic depending
+ * on payload length. this is only a struct layout draft
+ */
+struct ath10k_htc_frame {
+       struct ath10k_htc_hdr hdr;
+       union {
+               struct ath10k_htc_msg msg;
+               u8 payload[0];
+       };
+       struct ath10k_htc_record trailer[0];
+} __packed __aligned(4);
+
+
+/*******************/
+/* Host-side stuff */
+/*******************/
+
+enum ath10k_htc_svc_gid {
+       ATH10K_HTC_SVC_GRP_RSVD = 0,
+       ATH10K_HTC_SVC_GRP_WMI = 1,
+       ATH10K_HTC_SVC_GRP_NMI = 2,
+       ATH10K_HTC_SVC_GRP_HTT = 3,
+
+       ATH10K_HTC_SVC_GRP_TEST = 254,
+       ATH10K_HTC_SVC_GRP_LAST = 255,
+};
+
+#define SVC(group, idx) \
+       (int)(((int)(group) << 8) | (int)(idx))
+
+enum ath10k_htc_svc_id {
+       /* NOTE: service ID of 0x0000 is reserved and should never be used */
+       ATH10K_HTC_SVC_ID_RESERVED      = 0x0000,
+       ATH10K_HTC_SVC_ID_UNUSED        = ATH10K_HTC_SVC_ID_RESERVED,
+
+       ATH10K_HTC_SVC_ID_RSVD_CTRL     = SVC(ATH10K_HTC_SVC_GRP_RSVD, 1),
+       ATH10K_HTC_SVC_ID_WMI_CONTROL   = SVC(ATH10K_HTC_SVC_GRP_WMI, 0),
+       ATH10K_HTC_SVC_ID_WMI_DATA_BE   = SVC(ATH10K_HTC_SVC_GRP_WMI, 1),
+       ATH10K_HTC_SVC_ID_WMI_DATA_BK   = SVC(ATH10K_HTC_SVC_GRP_WMI, 2),
+       ATH10K_HTC_SVC_ID_WMI_DATA_VI   = SVC(ATH10K_HTC_SVC_GRP_WMI, 3),
+       ATH10K_HTC_SVC_ID_WMI_DATA_VO   = SVC(ATH10K_HTC_SVC_GRP_WMI, 4),
+
+       ATH10K_HTC_SVC_ID_NMI_CONTROL   = SVC(ATH10K_HTC_SVC_GRP_NMI, 0),
+       ATH10K_HTC_SVC_ID_NMI_DATA      = SVC(ATH10K_HTC_SVC_GRP_NMI, 1),
+
+       ATH10K_HTC_SVC_ID_HTT_DATA_MSG  = SVC(ATH10K_HTC_SVC_GRP_HTT, 0),
+
+       /* raw stream service (i.e. flash, tcmd, calibration apps) */
+       ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0),
+};
+
+#undef SVC
+
+enum ath10k_htc_ep_id {
+       ATH10K_HTC_EP_UNUSED = -1,
+       ATH10K_HTC_EP_0 = 0,
+       ATH10K_HTC_EP_1 = 1,
+       ATH10K_HTC_EP_2,
+       ATH10K_HTC_EP_3,
+       ATH10K_HTC_EP_4,
+       ATH10K_HTC_EP_5,
+       ATH10K_HTC_EP_6,
+       ATH10K_HTC_EP_7,
+       ATH10K_HTC_EP_8,
+       ATH10K_HTC_EP_COUNT,
+};
+
+struct ath10k_htc_ops {
+       void (*target_send_suspend_complete)(struct ath10k *ar);
+};
+
+struct ath10k_htc_ep_ops {
+       void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
+       void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
+};
+
+/* service connection information */
+struct ath10k_htc_svc_conn_req {
+       u16 service_id;
+       struct ath10k_htc_ep_ops ep_ops;
+       int max_send_queue_depth;
+};
+
+/* service connection response information */
+struct ath10k_htc_svc_conn_resp {
+       u8 buffer_len;
+       u8 actual_len;
+       enum ath10k_htc_ep_id eid;
+       unsigned int max_msg_len;
+       u8 connect_resp_code;
+};
+
+#define ATH10K_NUM_CONTROL_TX_BUFFERS 2
+#define ATH10K_HTC_MAX_LEN 4096
+#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256
+#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1*HZ)
+#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \
+                                       sizeof(struct ath10k_htc_hdr))
+#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1*HZ)
+
+struct ath10k_htc_ep {
+       struct ath10k_htc *htc;
+       enum ath10k_htc_ep_id eid;
+       enum ath10k_htc_svc_id service_id;
+       struct ath10k_htc_ep_ops ep_ops;
+
+       int max_tx_queue_depth;
+       int max_ep_message_len;
+       u8 ul_pipe_id;
+       u8 dl_pipe_id;
+       int ul_is_polled; /* call HIF to get tx completions */
+       int dl_is_polled; /* call HIF to fetch rx (not implemented) */
+
+       struct sk_buff_head tx_queue;
+
+       u8 seq_no; /* for debugging */
+       int tx_credits;
+       int tx_credit_size;
+       int tx_credits_per_max_message;
+       bool tx_credit_flow_enabled;
+
+       struct work_struct send_work;
+};
+
+struct ath10k_htc_svc_tx_credits {
+       u16 service_id;
+       u8  credit_allocation;
+};
+
+struct ath10k_htc {
+       struct ath10k *ar;
+       struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
+
+       /* protects endpoint and stopping fields */
+       spinlock_t tx_lock;
+
+       struct ath10k_htc_ops htc_ops;
+
+       u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN];
+       int control_resp_len;
+
+       struct completion ctl_resp;
+
+       int total_transmit_credits;
+       struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
+       int target_credit_size;
+
+       bool stopping;
+};
+
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+                                    struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_wait_target(struct ath10k_htc *htc);
+int ath10k_htc_start(struct ath10k_htc *htc);
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+                              struct ath10k_htc_svc_conn_req  *conn_req,
+                              struct ath10k_htc_svc_conn_resp *conn_resp);
+int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
+                   struct sk_buff *packet);
+void ath10k_htc_stop(struct ath10k_htc *htc);
+void ath10k_htc_destroy(struct ath10k_htc *htc);
+struct sk_buff *ath10k_htc_alloc_skb(int size);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
new file mode 100644 (file)
index 0000000..185a546
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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/slab.h>
+
+#include "htt.h"
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
+{
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+       int status;
+
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+
+       conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler;
+
+       /* connect to control service */
+       conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
+
+       status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+                                           &conn_resp);
+
+       if (status)
+               return status;
+
+       htt->eid = conn_resp.eid;
+
+       return 0;
+}
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+{
+       struct ath10k_htt *htt;
+       int ret;
+
+       htt = kzalloc(sizeof(*htt), GFP_KERNEL);
+       if (!htt)
+               return NULL;
+
+       htt->ar = ar;
+       htt->max_throughput_mbps = 800;
+
+       /*
+        * Connect to HTC service.
+        * This has to be done before calling ath10k_htt_rx_attach,
+        * since ath10k_htt_rx_attach involves sending a rx ring configure
+        * message to the target.
+        */
+       if (ath10k_htt_htc_attach(htt))
+               goto err_htc_attach;
+
+       ret = ath10k_htt_tx_attach(htt);
+       if (ret) {
+               ath10k_err("could not attach htt tx (%d)\n", ret);
+               goto err_htc_attach;
+       }
+
+       if (ath10k_htt_rx_attach(htt))
+               goto err_rx_attach;
+
+       /*
+        * Prefetch enough data to satisfy target
+        * classification engine.
+        * This is for LL chips. HL chips will probably
+        * transfer all frame in the tx fragment.
+        */
+       htt->prefetch_len =
+               36 + /* 802.11 + qos + ht */
+               4 + /* 802.1q */
+               8 + /* llc snap */
+               2; /* ip4 dscp or ip6 priority */
+
+       return htt;
+
+err_rx_attach:
+       ath10k_htt_tx_detach(htt);
+err_htc_attach:
+       kfree(htt);
+       return NULL;
+}
+
+#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
+
+static int ath10k_htt_verify_version(struct ath10k_htt *htt)
+{
+       ath10k_dbg(ATH10K_DBG_HTT,
+                  "htt target version %d.%d; host version %d.%d\n",
+                   htt->target_version_major,
+                   htt->target_version_minor,
+                   HTT_CURRENT_VERSION_MAJOR,
+                   HTT_CURRENT_VERSION_MINOR);
+
+       if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) {
+               ath10k_err("htt major versions are incompatible!\n");
+               return -ENOTSUPP;
+       }
+
+       if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR)
+               ath10k_warn("htt minor version differ but still compatible\n");
+
+       return 0;
+}
+
+int ath10k_htt_attach_target(struct ath10k_htt *htt)
+{
+       int status;
+
+       init_completion(&htt->target_version_received);
+
+       status = ath10k_htt_h2t_ver_req_msg(htt);
+       if (status)
+               return status;
+
+       status = wait_for_completion_timeout(&htt->target_version_received,
+                                               HTT_TARGET_VERSION_TIMEOUT_HZ);
+       if (status <= 0) {
+               ath10k_warn("htt version request timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       status = ath10k_htt_verify_version(htt);
+       if (status)
+               return status;
+
+       return ath10k_htt_send_rx_ring_cfg_ll(htt);
+}
+
+void ath10k_htt_detach(struct ath10k_htt *htt)
+{
+       ath10k_htt_rx_detach(htt);
+       ath10k_htt_tx_detach(htt);
+       kfree(htt);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
new file mode 100644 (file)
index 0000000..a7a7aa0
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _HTT_H_
+#define _HTT_H_
+
+#include <linux/bug.h>
+
+#include "core.h"
+#include "htc.h"
+#include "rx_desc.h"
+
+#define HTT_CURRENT_VERSION_MAJOR      2
+#define HTT_CURRENT_VERSION_MINOR      1
+
+enum htt_dbg_stats_type {
+       HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0,
+       HTT_DBG_STATS_RX_REORDER    = 1 << 1,
+       HTT_DBG_STATS_RX_RATE_INFO  = 1 << 2,
+       HTT_DBG_STATS_TX_PPDU_LOG   = 1 << 3,
+       HTT_DBG_STATS_TX_RATE_INFO  = 1 << 4,
+       /* bits 5-23 currently reserved */
+
+       HTT_DBG_NUM_STATS /* keep this last */
+};
+
+enum htt_h2t_msg_type { /* host-to-target */
+       HTT_H2T_MSG_TYPE_VERSION_REQ        = 0,
+       HTT_H2T_MSG_TYPE_TX_FRM             = 1,
+       HTT_H2T_MSG_TYPE_RX_RING_CFG        = 2,
+       HTT_H2T_MSG_TYPE_STATS_REQ          = 3,
+       HTT_H2T_MSG_TYPE_SYNC               = 4,
+       HTT_H2T_MSG_TYPE_AGGR_CFG           = 5,
+       HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6,
+       HTT_H2T_MSG_TYPE_MGMT_TX            = 7,
+
+       HTT_H2T_NUM_MSGS /* keep this last */
+};
+
+struct htt_cmd_hdr {
+       u8 msg_type;
+} __packed;
+
+struct htt_ver_req {
+       u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+} __packed;
+
+/*
+ * HTT tx MSDU descriptor
+ *
+ * The HTT tx MSDU descriptor is created by the host HTT SW for each
+ * tx MSDU.  The HTT tx MSDU descriptor contains the information that
+ * the target firmware needs for the FW's tx processing, particularly
+ * for creating the HW msdu descriptor.
+ * The same HTT tx descriptor is used for HL and LL systems, though
+ * a few fields within the tx descriptor are used only by LL or
+ * only by HL.
+ * The HTT tx descriptor is defined in two manners: by a struct with
+ * bitfields, and by a series of [dword offset, bit mask, bit shift]
+ * definitions.
+ * The target should use the struct def, for simplicitly and clarity,
+ * but the host shall use the bit-mast + bit-shift defs, to be endian-
+ * neutral.  Specifically, the host shall use the get/set macros built
+ * around the mask + shift defs.
+ */
+struct htt_data_tx_desc_frag {
+       __le32 paddr;
+       __le32 len;
+} __packed;
+
+enum htt_data_tx_desc_flags0 {
+       HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0,
+       HTT_DATA_TX_DESC_FLAGS0_NO_AGGR         = 1 << 1,
+       HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT      = 1 << 2,
+       HTT_DATA_TX_DESC_FLAGS0_NO_CLASSIFY     = 1 << 3,
+       HTT_DATA_TX_DESC_FLAGS0_RSVD0           = 1 << 4
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_MASK 0xE0
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_LSB 5
+};
+
+enum htt_data_tx_desc_flags1 {
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_BITS 6
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_MASK 0x003F
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_LSB  0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_BITS 5
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_MASK 0x07C0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_LSB  6
+       HTT_DATA_TX_DESC_FLAGS1_POSTPONED        = 1 << 11,
+       HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH    = 1 << 12,
+       HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13,
+       HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14,
+       HTT_DATA_TX_DESC_FLAGS1_RSVD1            = 1 << 15
+};
+
+enum htt_data_tx_ext_tid {
+       HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16,
+       HTT_DATA_TX_EXT_TID_MGMT                = 17,
+       HTT_DATA_TX_EXT_TID_INVALID             = 31
+};
+
+#define HTT_INVALID_PEERID 0xFFFF
+
+/*
+ * htt_data_tx_desc - used for data tx path
+ *
+ * Note: vdev_id irrelevant for pkt_type == raw and no_classify == 1.
+ *       ext_tid: for qos-data frames (0-15), see %HTT_DATA_TX_EXT_TID_
+ *                for special kinds of tids
+ *       postponed: only for HL hosts. indicates if this is a resend
+ *                  (HL hosts manage queues on the host )
+ *       more_in_batch: only for HL hosts. indicates if more packets are
+ *                      pending. this allows target to wait and aggregate
+ */
+struct htt_data_tx_desc {
+       u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
+       __le16 flags1; /* %HTT_DATA_TX_DESC_FLAGS1_ */
+       __le16 len;
+       __le16 id;
+       __le32 frags_paddr;
+       __le32 peerid;
+       u8 prefetch[0]; /* start of frame, for FW classification engine */
+} __packed;
+
+enum htt_rx_ring_flags {
+       HTT_RX_RING_FLAGS_MAC80211_HDR = 1 << 0,
+       HTT_RX_RING_FLAGS_MSDU_PAYLOAD = 1 << 1,
+       HTT_RX_RING_FLAGS_PPDU_START   = 1 << 2,
+       HTT_RX_RING_FLAGS_PPDU_END     = 1 << 3,
+       HTT_RX_RING_FLAGS_MPDU_START   = 1 << 4,
+       HTT_RX_RING_FLAGS_MPDU_END     = 1 << 5,
+       HTT_RX_RING_FLAGS_MSDU_START   = 1 << 6,
+       HTT_RX_RING_FLAGS_MSDU_END     = 1 << 7,
+       HTT_RX_RING_FLAGS_RX_ATTENTION = 1 << 8,
+       HTT_RX_RING_FLAGS_FRAG_INFO    = 1 << 9,
+       HTT_RX_RING_FLAGS_UNICAST_RX   = 1 << 10,
+       HTT_RX_RING_FLAGS_MULTICAST_RX = 1 << 11,
+       HTT_RX_RING_FLAGS_CTRL_RX      = 1 << 12,
+       HTT_RX_RING_FLAGS_MGMT_RX      = 1 << 13,
+       HTT_RX_RING_FLAGS_NULL_RX      = 1 << 14,
+       HTT_RX_RING_FLAGS_PHY_DATA_RX  = 1 << 15
+};
+
+struct htt_rx_ring_setup_ring {
+       __le32 fw_idx_shadow_reg_paddr;
+       __le32 rx_ring_base_paddr;
+       __le16 rx_ring_len; /* in 4-byte words */
+       __le16 rx_ring_bufsize; /* rx skb size - in bytes */
+       __le16 flags; /* %HTT_RX_RING_FLAGS_ */
+       __le16 fw_idx_init_val;
+
+       /* the following offsets are in 4-byte units */
+       __le16 mac80211_hdr_offset;
+       __le16 msdu_payload_offset;
+       __le16 ppdu_start_offset;
+       __le16 ppdu_end_offset;
+       __le16 mpdu_start_offset;
+       __le16 mpdu_end_offset;
+       __le16 msdu_start_offset;
+       __le16 msdu_end_offset;
+       __le16 rx_attention_offset;
+       __le16 frag_info_offset;
+} __packed;
+
+struct htt_rx_ring_setup_hdr {
+       u8 num_rings; /* supported values: 1, 2 */
+       __le16 rsvd0;
+} __packed;
+
+struct htt_rx_ring_setup {
+       struct htt_rx_ring_setup_hdr hdr;
+       struct htt_rx_ring_setup_ring rings[0];
+} __packed;
+
+/*
+ * htt_stats_req - request target to send specified statistics
+ *
+ * @msg_type: hardcoded %HTT_H2T_MSG_TYPE_STATS_REQ
+ * @upload_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *     so make sure its little-endian.
+ * @reset_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *     so make sure its little-endian.
+ * @cfg_val: stat_type specific configuration
+ * @stat_type: see %htt_dbg_stats_type
+ * @cookie_lsb: used for confirmation message from target->host
+ * @cookie_msb: ditto as %cookie
+ */
+struct htt_stats_req {
+       u8 upload_types[3];
+       u8 rsvd0;
+       u8 reset_types[3];
+       struct {
+               u8 mpdu_bytes;
+               u8 mpdu_num_msdus;
+               u8 msdu_bytes;
+       } __packed;
+       u8 stat_type;
+       __le32 cookie_lsb;
+       __le32 cookie_msb;
+} __packed;
+
+#define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff
+
+/*
+ * htt_oob_sync_req - request out-of-band sync
+ *
+ * The HTT SYNC tells the target to suspend processing of subsequent
+ * HTT host-to-target messages until some other target agent locally
+ * informs the target HTT FW that the current sync counter is equal to
+ * or greater than (in a modulo sense) the sync counter specified in
+ * the SYNC message.
+ *
+ * This allows other host-target components to synchronize their operation
+ * with HTT, e.g. to ensure that tx frames don't get transmitted until a
+ * security key has been downloaded to and activated by the target.
+ * In the absence of any explicit synchronization counter value
+ * specification, the target HTT FW will use zero as the default current
+ * sync value.
+ *
+ * The HTT target FW will suspend its host->target message processing as long
+ * as 0 < (in-band sync counter - out-of-band sync counter) & 0xff < 128.
+ */
+struct htt_oob_sync_req {
+       u8 sync_count;
+       __le16 rsvd0;
+} __packed;
+
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB  0
+
+struct htt_aggr_conf {
+       u8 max_num_ampdu_subframes;
+       union {
+               /* dont use bitfields; undefined behaviour */
+               u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
+               u8 max_num_amsdu_subframes:5;
+       } __packed;
+} __packed;
+
+#define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
+
+struct htt_mgmt_tx_desc {
+       u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+       __le32 msdu_paddr;
+       __le32 desc_id;
+       __le32 len;
+       __le32 vdev_id;
+       u8 hdr[HTT_MGMT_FRM_HDR_DOWNLOAD_LEN];
+} __packed;
+
+enum htt_mgmt_tx_status {
+       HTT_MGMT_TX_STATUS_OK    = 0,
+       HTT_MGMT_TX_STATUS_RETRY = 1,
+       HTT_MGMT_TX_STATUS_DROP  = 2
+};
+
+/*=== target -> host messages ===============================================*/
+
+
+enum htt_t2h_msg_type {
+       HTT_T2H_MSG_TYPE_VERSION_CONF           = 0x0,
+       HTT_T2H_MSG_TYPE_RX_IND                 = 0x1,
+       HTT_T2H_MSG_TYPE_RX_FLUSH               = 0x2,
+       HTT_T2H_MSG_TYPE_PEER_MAP               = 0x3,
+       HTT_T2H_MSG_TYPE_PEER_UNMAP             = 0x4,
+       HTT_T2H_MSG_TYPE_RX_ADDBA               = 0x5,
+       HTT_T2H_MSG_TYPE_RX_DELBA               = 0x6,
+       HTT_T2H_MSG_TYPE_TX_COMPL_IND           = 0x7,
+       HTT_T2H_MSG_TYPE_PKTLOG                 = 0x8,
+       HTT_T2H_MSG_TYPE_STATS_CONF             = 0x9,
+       HTT_T2H_MSG_TYPE_RX_FRAG_IND            = 0xa,
+       HTT_T2H_MSG_TYPE_SEC_IND                = 0xb,
+       HTT_T2H_MSG_TYPE_RC_UPDATE_IND          = 0xc,
+       HTT_T2H_MSG_TYPE_TX_INSPECT_IND         = 0xd,
+       HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION     = 0xe,
+       HTT_T2H_MSG_TYPE_TEST,
+       /* keep this last */
+       HTT_T2H_NUM_MSGS
+};
+
+/*
+ * htt_resp_hdr - header for target-to-host messages
+ *
+ * msg_type: see htt_t2h_msg_type
+ */
+struct htt_resp_hdr {
+       u8 msg_type;
+} __packed;
+
+#define HTT_RESP_HDR_MSG_TYPE_OFFSET 0
+#define HTT_RESP_HDR_MSG_TYPE_MASK   0xff
+#define HTT_RESP_HDR_MSG_TYPE_LSB    0
+
+/* htt_ver_resp - response sent for htt_ver_req */
+struct htt_ver_resp {
+       u8 minor;
+       u8 major;
+       u8 rsvd0;
+} __packed;
+
+struct htt_mgmt_tx_completion {
+       u8 rsvd0;
+       u8 rsvd1;
+       u8 rsvd2;
+       __le32 desc_id;
+       __le32 status;
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_EXT_TID_MASK  (0x3F)
+#define HTT_RX_INDICATION_INFO0_EXT_TID_LSB   (0)
+#define HTT_RX_INDICATION_INFO0_FLUSH_VALID   (1 << 6)
+#define HTT_RX_INDICATION_INFO0_RELEASE_VALID (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_MASK   0x0000003F
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_LSB    0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_MASK     0x00000FC0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_LSB      6
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_MASK 0x0003F000
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_LSB  12
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_MASK   0x00FC0000
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_LSB    18
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK     0xFF000000
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB      24
+
+struct htt_rx_indication_hdr {
+       u8 info0; /* %HTT_RX_INDICATION_INFO0_ */
+       __le16 peer_id;
+       __le32 info1; /* %HTT_RX_INDICATION_INFO1_ */
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_PHY_ERR_VALID    (1 << 0)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_MASK (0x1E)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_LSB  (1)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK  (1 << 5)
+#define HTT_RX_INDICATION_INFO0_END_VALID        (1 << 6)
+#define HTT_RX_INDICATION_INFO0_START_VALID      (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_MASK    0x00FFFFFF
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_LSB     0
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_MASK 0xFF000000
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_LSB  24
+
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_MASK 0x00FFFFFF
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_LSB  0
+#define HTT_RX_INDICATION_INFO2_SERVICE_MASK    0xFF000000
+#define HTT_RX_INDICATION_INFO2_SERVICE_LSB     24
+
+enum htt_rx_legacy_rate {
+       HTT_RX_OFDM_48 = 0,
+       HTT_RX_OFDM_24 = 1,
+       HTT_RX_OFDM_12,
+       HTT_RX_OFDM_6,
+       HTT_RX_OFDM_54,
+       HTT_RX_OFDM_36,
+       HTT_RX_OFDM_18,
+       HTT_RX_OFDM_9,
+
+       /* long preamble */
+       HTT_RX_CCK_11_LP = 0,
+       HTT_RX_CCK_5_5_LP = 1,
+       HTT_RX_CCK_2_LP,
+       HTT_RX_CCK_1_LP,
+       /* short preamble */
+       HTT_RX_CCK_11_SP,
+       HTT_RX_CCK_5_5_SP,
+       HTT_RX_CCK_2_SP
+};
+
+enum htt_rx_legacy_rate_type {
+       HTT_RX_LEGACY_RATE_OFDM = 0,
+       HTT_RX_LEGACY_RATE_CCK
+};
+
+enum htt_rx_preamble_type {
+       HTT_RX_LEGACY        = 0x4,
+       HTT_RX_HT            = 0x8,
+       HTT_RX_HT_WITH_TXBF  = 0x9,
+       HTT_RX_VHT           = 0xC,
+       HTT_RX_VHT_WITH_TXBF = 0xD,
+};
+
+/*
+ * Fields: phy_err_valid, phy_err_code, tsf,
+ * usec_timestamp, sub_usec_timestamp
+ * ..are valid only if end_valid == 1.
+ *
+ * Fields: rssi_chains, legacy_rate_type,
+ * legacy_rate_cck, preamble_type, service,
+ * vht_sig_*
+ * ..are valid only if start_valid == 1;
+ */
+struct htt_rx_indication_ppdu {
+       u8 combined_rssi;
+       u8 sub_usec_timestamp;
+       u8 phy_err_code;
+       u8 info0; /* HTT_RX_INDICATION_INFO0_ */
+       struct {
+               u8 pri20_db;
+               u8 ext20_db;
+               u8 ext40_db;
+               u8 ext80_db;
+       } __packed rssi_chains[4];
+       __le32 tsf;
+       __le32 usec_timestamp;
+       __le32 info1; /* HTT_RX_INDICATION_INFO1_ */
+       __le32 info2; /* HTT_RX_INDICATION_INFO2_ */
+} __packed;
+
+enum htt_rx_mpdu_status {
+       HTT_RX_IND_MPDU_STATUS_UNKNOWN = 0x0,
+       HTT_RX_IND_MPDU_STATUS_OK,
+       HTT_RX_IND_MPDU_STATUS_ERR_FCS,
+       HTT_RX_IND_MPDU_STATUS_ERR_DUP,
+       HTT_RX_IND_MPDU_STATUS_ERR_REPLAY,
+       HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER,
+       /* only accept EAPOL frames */
+       HTT_RX_IND_MPDU_STATUS_UNAUTH_PEER,
+       HTT_RX_IND_MPDU_STATUS_OUT_OF_SYNC,
+       /* Non-data in promiscous mode */
+       HTT_RX_IND_MPDU_STATUS_MGMT_CTRL,
+       HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR,
+       HTT_RX_IND_MPDU_STATUS_DECRYPT_ERR,
+       HTT_RX_IND_MPDU_STATUS_MPDU_LENGTH_ERR,
+       HTT_RX_IND_MPDU_STATUS_ENCRYPT_REQUIRED_ERR,
+       HTT_RX_IND_MPDU_STATUS_PRIVACY_ERR,
+
+       /*
+        * MISC: discard for unspecified reasons.
+        * Leave this enum value last.
+        */
+       HTT_RX_IND_MPDU_STATUS_ERR_MISC = 0xFF
+};
+
+struct htt_rx_indication_mpdu_range {
+       u8 mpdu_count;
+       u8 mpdu_range_status; /* %htt_rx_mpdu_status */
+       u8 pad0;
+       u8 pad1;
+} __packed;
+
+struct htt_rx_indication_prefix {
+       __le16 fw_rx_desc_bytes;
+       u8 pad0;
+       u8 pad1;
+};
+
+struct htt_rx_indication {
+       struct htt_rx_indication_hdr hdr;
+       struct htt_rx_indication_ppdu ppdu;
+       struct htt_rx_indication_prefix prefix;
+
+       /*
+        * the following fields are both dynamically sized, so
+        * take care addressing them
+        */
+
+       /* the size of this is %fw_rx_desc_bytes */
+       struct fw_rx_desc_base fw_desc;
+
+       /*
+        * %mpdu_ranges starts after &%prefix + roundup(%fw_rx_desc_bytes, 4)
+        * and has %num_mpdu_ranges elements.
+        */
+       struct htt_rx_indication_mpdu_range mpdu_ranges[0];
+} __packed;
+
+static inline struct htt_rx_indication_mpdu_range *
+               htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind)
+{
+       void *ptr = rx_ind;
+
+       ptr += sizeof(rx_ind->hdr)
+            + sizeof(rx_ind->ppdu)
+            + sizeof(rx_ind->prefix)
+            + roundup(__le16_to_cpu(rx_ind->prefix.fw_rx_desc_bytes), 4);
+       return ptr;
+}
+
+enum htt_rx_flush_mpdu_status {
+       HTT_RX_FLUSH_MPDU_DISCARD = 0,
+       HTT_RX_FLUSH_MPDU_REORDER = 1,
+};
+
+/*
+ * htt_rx_flush - discard or reorder given range of mpdus
+ *
+ * Note: host must check if all sequence numbers between
+ *     [seq_num_start, seq_num_end-1] are valid.
+ */
+struct htt_rx_flush {
+       __le16 peer_id;
+       u8 tid;
+       u8 rsvd0;
+       u8 mpdu_status; /* %htt_rx_flush_mpdu_status */
+       u8 seq_num_start; /* it is 6 LSBs of 802.11 seq no */
+       u8 seq_num_end; /* it is 6 LSBs of 802.11 seq no */
+};
+
+struct htt_rx_peer_map {
+       u8 vdev_id;
+       __le16 peer_id;
+       u8 addr[6];
+       u8 rsvd0;
+       u8 rsvd1;
+} __packed;
+
+struct htt_rx_peer_unmap {
+       u8 rsvd0;
+       __le16 peer_id;
+} __packed;
+
+enum htt_security_types {
+       HTT_SECURITY_NONE,
+       HTT_SECURITY_WEP128,
+       HTT_SECURITY_WEP104,
+       HTT_SECURITY_WEP40,
+       HTT_SECURITY_TKIP,
+       HTT_SECURITY_TKIP_NOMIC,
+       HTT_SECURITY_AES_CCMP,
+       HTT_SECURITY_WAPI,
+
+       HTT_NUM_SECURITY_TYPES /* keep this last! */
+};
+
+enum htt_security_flags {
+#define HTT_SECURITY_TYPE_MASK 0x7F
+#define HTT_SECURITY_TYPE_LSB  0
+       HTT_SECURITY_IS_UNICAST = 1 << 7
+};
+
+struct htt_security_indication {
+       union {
+               /* dont use bitfields; undefined behaviour */
+               u8 flags; /* %htt_security_flags */
+               struct {
+                       u8 security_type:7, /* %htt_security_types */
+                          is_unicast:1;
+               } __packed;
+       } __packed;
+       __le16 peer_id;
+       u8 michael_key[8];
+       u8 wapi_rsc[16];
+} __packed;
+
+#define HTT_RX_BA_INFO0_TID_MASK     0x000F
+#define HTT_RX_BA_INFO0_TID_LSB      0
+#define HTT_RX_BA_INFO0_PEER_ID_MASK 0xFFF0
+#define HTT_RX_BA_INFO0_PEER_ID_LSB  4
+
+struct htt_rx_addba {
+       u8 window_size;
+       __le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+struct htt_rx_delba {
+       u8 rsvd0;
+       __le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+enum htt_data_tx_status {
+       HTT_DATA_TX_STATUS_OK            = 0,
+       HTT_DATA_TX_STATUS_DISCARD       = 1,
+       HTT_DATA_TX_STATUS_NO_ACK        = 2,
+       HTT_DATA_TX_STATUS_POSTPONE      = 3, /* HL only */
+       HTT_DATA_TX_STATUS_DOWNLOAD_FAIL = 128
+};
+
+enum htt_data_tx_flags {
+#define HTT_DATA_TX_STATUS_MASK 0x07
+#define HTT_DATA_TX_STATUS_LSB  0
+#define HTT_DATA_TX_TID_MASK    0x78
+#define HTT_DATA_TX_TID_LSB     3
+       HTT_DATA_TX_TID_INVALID = 1 << 7
+};
+
+#define HTT_TX_COMPL_INV_MSDU_ID 0xFFFF
+
+struct htt_data_tx_completion {
+       union {
+               u8 flags;
+               struct {
+                       u8 status:3,
+                          tid:4,
+                          tid_invalid:1;
+               } __packed;
+       } __packed;
+       u8 num_msdus;
+       u8 rsvd0;
+       __le16 msdus[0]; /* variable length based on %num_msdus */
+} __packed;
+
+struct htt_tx_compl_ind_base {
+       u32 hdr;
+       u16 payload[1/*or more*/];
+} __packed;
+
+struct htt_rc_tx_done_params {
+       u32 rate_code;
+       u32 rate_code_flags;
+       u32 flags;
+       u32 num_enqued; /* 1 for non-AMPDU */
+       u32 num_retries;
+       u32 num_failed; /* for AMPDU */
+       u32 ack_rssi;
+       u32 time_stamp;
+       u32 is_probe;
+};
+
+struct htt_rc_update {
+       u8 vdev_id;
+       __le16 peer_id;
+       u8 addr[6];
+       u8 num_elems;
+       u8 rsvd0;
+       struct htt_rc_tx_done_params params[0]; /* variable length %num_elems */
+} __packed;
+
+/* see htt_rx_indication for similar fields and descriptions */
+struct htt_rx_fragment_indication {
+       union {
+               u8 info0; /* %HTT_RX_FRAG_IND_INFO0_ */
+               struct {
+                       u8 ext_tid:5,
+                          flush_valid:1;
+               } __packed;
+       } __packed;
+       __le16 peer_id;
+       __le32 info1; /* %HTT_RX_FRAG_IND_INFO1_ */
+       __le16 fw_rx_desc_bytes;
+       __le16 rsvd0;
+
+       u8 fw_msdu_rx_desc[0];
+} __packed;
+
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK     0x1F
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB      0
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_LSB  5
+
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_MASK 0x0000003F
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_LSB  0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK   0x00000FC0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB    6
+
+/*
+ * target -> host test message definition
+ *
+ * The following field definitions describe the format of the test
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header, followed by a variable
+ * number of 32-bit integer values, followed by a variable number
+ * of 8-bit character values.
+ *
+ * |31                         16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |          num chars          |   num ints   |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                           int 0                           |
+ * |-----------------------------------------------------------|
+ * |                           int 1                           |
+ * |-----------------------------------------------------------|
+ * |                            ...                            |
+ * |-----------------------------------------------------------|
+ * |    char 3    |    char 2    |    char 1    |    char 0    |
+ * |-----------------------------------------------------------|
+ * |              |              |      ...     |    char 4    |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_TEST
+ *   - NUM_INTS
+ *     Bits 15:8
+ *     Purpose: indicate how many 32-bit integers follow the message header
+ *   - NUM_CHARS
+ *     Bits 31:16
+ *     Purpose: indicate how many 8-bit charaters follow the series of integers
+ */
+struct htt_rx_test {
+       u8 num_ints;
+       __le16 num_chars;
+
+       /* payload consists of 2 lists:
+        *  a) num_ints * sizeof(__le32)
+        *  b) num_chars * sizeof(u8) aligned to 4bytes */
+       u8 payload[0];
+} __packed;
+
+static inline __le32 *htt_rx_test_get_ints(struct htt_rx_test *rx_test)
+{
+       return (__le32 *)rx_test->payload;
+}
+
+static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test)
+{
+       return rx_test->payload + (rx_test->num_ints * sizeof(__le32));
+}
+
+/*
+ * target -> host packet log message
+ *
+ * The following field definitions describe the format of the packet log
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header,followed by a variable number
+ * of 32-bit character values.
+ *
+ * |31          24|23          16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |              |              |              |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                        payload                            |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_PACKETLOG
+ */
+struct htt_pktlog_msg {
+       u8 pad[3];
+       __le32 payload[1 /* or more */];
+} __packed;
+
+struct htt_dbg_stats_rx_reorder_stats {
+       /* Non QoS MPDUs received */
+       __le32 deliver_non_qos;
+
+       /* MPDUs received in-order */
+       __le32 deliver_in_order;
+
+       /* Flush due to reorder timer expired */
+       __le32 deliver_flush_timeout;
+
+       /* Flush due to move out of window */
+       __le32 deliver_flush_oow;
+
+       /* Flush due to DELBA */
+       __le32 deliver_flush_delba;
+
+       /* MPDUs dropped due to FCS error */
+       __le32 fcs_error;
+
+       /* MPDUs dropped due to monitor mode non-data packet */
+       __le32 mgmt_ctrl;
+
+       /* MPDUs dropped due to invalid peer */
+       __le32 invalid_peer;
+
+       /* MPDUs dropped due to duplication (non aggregation) */
+       __le32 dup_non_aggr;
+
+       /* MPDUs dropped due to processed before */
+       __le32 dup_past;
+
+       /* MPDUs dropped due to duplicate in reorder queue */
+       __le32 dup_in_reorder;
+
+       /* Reorder timeout happened */
+       __le32 reorder_timeout;
+
+       /* invalid bar ssn */
+       __le32 invalid_bar_ssn;
+
+       /* reorder reset due to bar ssn */
+       __le32 ssn_reset;
+};
+
+struct htt_dbg_stats_wal_tx_stats {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+} __packed;
+
+struct htt_dbg_stats_wal_rx_stats {
+       /* Cnts any change in ring routing mid-ppdu */
+       __le32 mid_ppdu_route_change;
+
+       /* Total number of statuses processed */
+       __le32 status_rcvd;
+
+       /* Extra frags on rings 0-3 */
+       __le32 r0_frags;
+       __le32 r1_frags;
+       __le32 r2_frags;
+       __le32 r3_frags;
+
+       /* MSDUs / MPDUs delivered to HTT */
+       __le32 htt_msdus;
+       __le32 htt_mpdus;
+
+       /* MSDUs / MPDUs delivered to local stack */
+       __le32 loc_msdus;
+       __le32 loc_mpdus;
+
+       /* AMSDUs that have more MSDUs than the status ring size */
+       __le32 oversize_amsdu;
+
+       /* Number of PHY errors */
+       __le32 phy_errs;
+
+       /* Number of PHY errors drops */
+       __le32 phy_err_drop;
+
+       /* Number of mpdu errors - FCS, MIC, ENC etc. */
+       __le32 mpdu_errs;
+} __packed;
+
+struct htt_dbg_stats_wal_peer_stats {
+       __le32 dummy; /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+} __packed;
+
+struct htt_dbg_stats_wal_pdev_txrx {
+       struct htt_dbg_stats_wal_tx_stats tx_stats;
+       struct htt_dbg_stats_wal_rx_stats rx_stats;
+       struct htt_dbg_stats_wal_peer_stats peer_stats;
+} __packed;
+
+struct htt_dbg_stats_rx_rate_info {
+       __le32 mcs[10];
+       __le32 sgi[10];
+       __le32 nss[4];
+       __le32 stbc[10];
+       __le32 bw[3];
+       __le32 pream[6];
+       __le32 ldpc;
+       __le32 txbf;
+};
+
+/*
+ * htt_dbg_stats_status -
+ * present -     The requested stats have been delivered in full.
+ *               This indicates that either the stats information was contained
+ *               in its entirety within this message, or else this message
+ *               completes the delivery of the requested stats info that was
+ *               partially delivered through earlier STATS_CONF messages.
+ * partial -     The requested stats have been delivered in part.
+ *               One or more subsequent STATS_CONF messages with the same
+ *               cookie value will be sent to deliver the remainder of the
+ *               information.
+ * error -       The requested stats could not be delivered, for example due
+ *               to a shortage of memory to construct a message holding the
+ *               requested stats.
+ * invalid -     The requested stat type is either not recognized, or the
+ *               target is configured to not gather the stats type in question.
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * series_done - This special value indicates that no further stats info
+ *               elements are present within a series of stats info elems
+ *               (within a stats upload confirmation message).
+ */
+enum htt_dbg_stats_status {
+       HTT_DBG_STATS_STATUS_PRESENT     = 0,
+       HTT_DBG_STATS_STATUS_PARTIAL     = 1,
+       HTT_DBG_STATS_STATUS_ERROR       = 2,
+       HTT_DBG_STATS_STATUS_INVALID     = 3,
+       HTT_DBG_STATS_STATUS_SERIES_DONE = 7
+};
+
+/*
+ * target -> host statistics upload
+ *
+ * The following field definitions describe the format of the HTT target
+ * to host stats upload confirmation message.
+ * The message contains a cookie echoed from the HTT host->target stats
+ * upload request, which identifies which request the confirmation is
+ * for, and a series of tag-length-value stats information elements.
+ * The tag-length header for each stats info element also includes a
+ * status field, to indicate whether the request for the stat type in
+ * question was fully met, partially met, unable to be met, or invalid
+ * (if the stat type in question is disabled in the target).
+ * A special value of all 1's in this status field is used to indicate
+ * the end of the series of stats info elements.
+ *
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * |                  reserved                  |    msg type   |
+ * |------------------------------------------------------------|
+ * |                        cookie LSBs                         |
+ * |------------------------------------------------------------|
+ * |                        cookie MSBs                         |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |              n/a            |   reserved   | 111 |   n/a   |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Purpose: identifies this is a statistics upload confirmation message
+ *    Value: 0x9
+ *  - COOKIE_LSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: LSBs of the opaque cookie specified by the host-side requestor
+ *  - COOKIE_MSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: MSBs of the opaque cookie specified by the host-side requestor
+ *
+ * Stats Information Element tag-length header fields:
+ *  - STAT_TYPE
+ *    Bits 4:0
+ *    Purpose: identifies the type of statistics info held in the
+ *        following information element
+ *    Value: htt_dbg_stats_type
+ *  - STATUS
+ *    Bits 7:5
+ *    Purpose: indicate whether the requested stats are present
+ *    Value: htt_dbg_stats_status, including a special value (0x7) to mark
+ *        the completion of the stats entry series
+ *  - LENGTH
+ *    Bits 31:16
+ *    Purpose: indicate the stats information size
+ *    Value: This field specifies the number of bytes of stats information
+ *       that follows the element tag-length header.
+ *       It is expected but not required that this length is a multiple of
+ *       4 bytes.  Even if the length is not an integer multiple of 4, the
+ *       subsequent stats entry header will begin on a 4-byte aligned
+ *       boundary.
+ */
+
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_MASK 0x1F
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_LSB  0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_MASK    0xE0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_LSB     5
+
+struct htt_stats_conf_item {
+       union {
+               u8 info;
+               struct {
+                       u8 stat_type:5; /* %HTT_DBG_STATS_ */
+                       u8 status:3; /* %HTT_DBG_STATS_STATUS_ */
+               } __packed;
+       } __packed;
+       u8 pad;
+       __le16 length;
+       u8 payload[0]; /* roundup(length, 4) long */
+} __packed;
+
+struct htt_stats_conf {
+       u8 pad[3];
+       __le32 cookie_lsb;
+       __le32 cookie_msb;
+
+       /* each item has variable length! */
+       struct htt_stats_conf_item items[0];
+} __packed;
+
+static inline struct htt_stats_conf_item *htt_stats_conf_next_item(
+                                       const struct htt_stats_conf_item *item)
+{
+       return (void *)item + sizeof(*item) + roundup(item->length, 4);
+}
+/*
+ * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank
+ *
+ * The following field definitions describe the format of the HTT host
+ * to target frag_desc/msdu_ext bank configuration message.
+ * The message contains the based address and the min and max id of the
+ * MSDU_EXT/FRAG_DESC that will be used by the HTT to map MSDU DESC and
+ * MSDU_EXT/FRAG_DESC.
+ * HTT will use id in HTT descriptor instead sending the frag_desc_ptr.
+ * For QCA988X HW the firmware will use fragment_desc_ptr but in WIFI2.0
+ * the hardware does the mapping/translation.
+ *
+ * Total banks that can be configured is configured to 16.
+ *
+ * This should be called before any TX has be initiated by the HTT
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * | DESC_SIZE    |  NUM_BANKS   | RES |SWP|pdev|    msg type   |
+ * |------------------------------------------------------------|
+ * |                     BANK0_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |                    BANK15_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |       BANK0_MAX_ID          |       BANK0_MIN_ID           |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |       BANK15_MAX_ID         |       BANK15_MIN_ID          |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Value: 0x6
+ *  - BANKx_BASE_ADDRESS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to specify the base address of the MSDU_EXT
+ *         bank physical/bus address.
+ *  - BANKx_MIN_ID
+ *    Bits 15:0
+ *    Purpose: Provide a mechanism to specify the min index that needs to
+ *          mapped.
+ *  - BANKx_MAX_ID
+ *    Bits 31:16
+ *    Purpose: Provide a mechanism to specify the max index that needs to
+ *
+ */
+struct htt_frag_desc_bank_id {
+       __le16 bank_min_id;
+       __le16 bank_max_id;
+} __packed;
+
+/* real is 16 but it wouldn't fit in the max htt message size
+ * so we use a conservatively safe value for now */
+#define HTT_FRAG_DESC_BANK_MAX 4
+
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB  0
+#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP         (1 << 2)
+
+struct htt_frag_desc_bank_cfg {
+       u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */
+       u8 num_banks;
+       u8 desc_size;
+       __le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX];
+       struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX];
+} __packed;
+
+union htt_rx_pn_t {
+       /* WEP: 24-bit PN */
+       u32 pn24;
+
+       /* TKIP or CCMP: 48-bit PN */
+       u_int64_t pn48;
+
+       /* WAPI: 128-bit PN */
+       u_int64_t pn128[2];
+};
+
+struct htt_cmd {
+       struct htt_cmd_hdr hdr;
+       union {
+               struct htt_ver_req ver_req;
+               struct htt_mgmt_tx_desc mgmt_tx;
+               struct htt_data_tx_desc data_tx;
+               struct htt_rx_ring_setup rx_setup;
+               struct htt_stats_req stats_req;
+               struct htt_oob_sync_req oob_sync_req;
+               struct htt_aggr_conf aggr_conf;
+               struct htt_frag_desc_bank_cfg frag_desc_bank_cfg;
+       };
+} __packed;
+
+struct htt_resp {
+       struct htt_resp_hdr hdr;
+       union {
+               struct htt_ver_resp ver_resp;
+               struct htt_mgmt_tx_completion mgmt_tx_completion;
+               struct htt_data_tx_completion data_tx_completion;
+               struct htt_rx_indication rx_ind;
+               struct htt_rx_fragment_indication rx_frag_ind;
+               struct htt_rx_peer_map peer_map;
+               struct htt_rx_peer_unmap peer_unmap;
+               struct htt_rx_flush rx_flush;
+               struct htt_rx_addba rx_addba;
+               struct htt_rx_delba rx_delba;
+               struct htt_security_indication security_indication;
+               struct htt_rc_update rc_update;
+               struct htt_rx_test rx_test;
+               struct htt_pktlog_msg pktlog_msg;
+               struct htt_stats_conf stats_conf;
+       };
+} __packed;
+
+
+/*** host side structures follow ***/
+
+struct htt_tx_done {
+       u32 msdu_id;
+       bool discard;
+       bool no_ack;
+};
+
+struct htt_peer_map_event {
+       u8 vdev_id;
+       u16 peer_id;
+       u8 addr[ETH_ALEN];
+};
+
+struct htt_peer_unmap_event {
+       u16 peer_id;
+};
+
+struct htt_rx_info {
+       struct sk_buff *skb;
+       enum htt_rx_mpdu_status status;
+       enum htt_rx_mpdu_encrypt_type encrypt_type;
+       s8 signal;
+       struct {
+               u8 info0;
+               u32 info1;
+               u32 info2;
+       } rate;
+       bool fcs_err;
+};
+
+struct ath10k_htt {
+       struct ath10k *ar;
+       enum ath10k_htc_ep_id eid;
+
+       int max_throughput_mbps;
+       u8 target_version_major;
+       u8 target_version_minor;
+       struct completion target_version_received;
+
+       struct {
+               /*
+                * Ring of network buffer objects - This ring is
+                * used exclusively by the host SW. This ring
+                * mirrors the dev_addrs_ring that is shared
+                * between the host SW and the MAC HW. The host SW
+                * uses this netbufs ring to locate the network
+                * buffer objects whose data buffers the HW has
+                * filled.
+                */
+               struct sk_buff **netbufs_ring;
+               /*
+                * Ring of buffer addresses -
+                * This ring holds the "physical" device address of the
+                * rx buffers the host SW provides for the MAC HW to
+                * fill.
+                */
+               __le32 *paddrs_ring;
+
+               /*
+                * Base address of ring, as a "physical" device address
+                * rather than a CPU address.
+                */
+               dma_addr_t base_paddr;
+
+               /* how many elems in the ring (power of 2) */
+               int size;
+
+               /* size - 1 */
+               unsigned size_mask;
+
+               /* how many rx buffers to keep in the ring */
+               int fill_level;
+
+               /* how many rx buffers (full+empty) are in the ring */
+               int fill_cnt;
+
+               /*
+                * alloc_idx - where HTT SW has deposited empty buffers
+                * This is allocated in consistent mem, so that the FW can
+                * read this variable, and program the HW's FW_IDX reg with
+                * the value of this shadow register.
+                */
+               struct {
+                       __le32 *vaddr;
+                       dma_addr_t paddr;
+               } alloc_idx;
+
+               /* where HTT SW has processed bufs filled by rx MAC DMA */
+               struct {
+                       unsigned msdu_payld;
+               } sw_rd_idx;
+
+               /*
+                * refill_retry_timer - timer triggered when the ring is
+                * not refilled to the level expected
+                */
+               struct timer_list refill_retry_timer;
+
+               /* Protects access to all rx ring buffer state variables */
+               spinlock_t lock;
+       } rx_ring;
+
+       unsigned int prefetch_len;
+
+       /* Protects access to %pending_tx, %used_msdu_ids */
+       spinlock_t tx_lock;
+       int max_num_pending_tx;
+       int num_pending_tx;
+       struct sk_buff **pending_tx;
+       unsigned long *used_msdu_ids; /* bitmap */
+       wait_queue_head_t empty_tx_wq;
+
+       /* set if host-fw communication goes haywire
+        * used to avoid further failures */
+       bool rx_confused;
+};
+
+#define RX_HTT_HDR_STATUS_LEN 64
+
+/* This structure layout is programmed via rx ring setup
+ * so that FW knows how to transfer the rx descriptor to the host.
+ * Buffers like this are placed on the rx ring. */
+struct htt_rx_desc {
+       union {
+               /* This field is filled on the host using the msdu buffer
+                * from htt_rx_indication */
+               struct fw_rx_desc_base fw_desc;
+               u32 pad;
+       } __packed;
+       struct {
+               struct rx_attention attention;
+               struct rx_frag_info frag_info;
+               struct rx_mpdu_start mpdu_start;
+               struct rx_msdu_start msdu_start;
+               struct rx_msdu_end msdu_end;
+               struct rx_mpdu_end mpdu_end;
+               struct rx_ppdu_start ppdu_start;
+               struct rx_ppdu_end ppdu_end;
+       } __packed;
+       u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN];
+       u8 msdu_payload[0];
+};
+
+#define HTT_RX_DESC_ALIGN 8
+
+#define HTT_MAC_ADDR_LEN 6
+
+/*
+ * FIX THIS
+ * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size,
+ * rounded up to a cache line size.
+ */
+#define HTT_RX_BUF_SIZE 1920
+#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
+
+/*
+ * DMA_MAP expects the buffer to be an integral number of cache lines.
+ * Rather than checking the actual cache line size, this code makes a
+ * conservative estimate of what the cache line size could be.
+ */
+#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */
+#define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach_target(struct ath10k_htt *htt);
+void ath10k_htt_detach(struct ath10k_htt *htt);
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt);
+void ath10k_htt_tx_detach(struct ath10k_htt *htt);
+int ath10k_htt_rx_attach(struct ath10k_htt *htt);
+void ath10k_htt_rx_detach(struct ath10k_htt *htt);
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
new file mode 100644 (file)
index 0000000..de058d7
--- /dev/null
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 "htc.h"
+#include "htt.h"
+#include "txrx.h"
+#include "debug.h"
+
+#include <linux/log2.h>
+
+/* slightly larger than one large A-MPDU */
+#define HTT_RX_RING_SIZE_MIN 128
+
+/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
+#define HTT_RX_RING_SIZE_MAX 2048
+
+#define HTT_RX_AVG_FRM_BYTES 1000
+
+/* ms, very conservative */
+#define HTT_RX_HOST_LATENCY_MAX_MS 20
+
+/* ms, conservative */
+#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+
+/* when under memory pressure rx ring refill may fail and needs a retry */
+#define HTT_RX_RING_REFILL_RETRY_MS 50
+
+static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
+{
+       int size;
+
+       /*
+        * It is expected that the host CPU will typically be able to
+        * service the rx indication from one A-MPDU before the rx
+        * indication from the subsequent A-MPDU happens, roughly 1-2 ms
+        * later. However, the rx ring should be sized very conservatively,
+        * to accomodate the worst reasonable delay before the host CPU
+        * services a rx indication interrupt.
+        *
+        * The rx ring need not be kept full of empty buffers. In theory,
+        * the htt host SW can dynamically track the low-water mark in the
+        * rx ring, and dynamically adjust the level to which the rx ring
+        * is filled with empty buffers, to dynamically meet the desired
+        * low-water mark.
+        *
+        * In contrast, it's difficult to resize the rx ring itself, once
+        * it's in use. Thus, the ring itself should be sized very
+        * conservatively, while the degree to which the ring is filled
+        * with empty buffers should be sized moderately conservatively.
+        */
+
+       /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+       size =
+           htt->max_throughput_mbps +
+           1000  /
+           (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
+
+       if (size < HTT_RX_RING_SIZE_MIN)
+               size = HTT_RX_RING_SIZE_MIN;
+
+       if (size > HTT_RX_RING_SIZE_MAX)
+               size = HTT_RX_RING_SIZE_MAX;
+
+       size = roundup_pow_of_two(size);
+
+       return size;
+}
+
+static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
+{
+       int size;
+
+       /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+       size =
+           htt->max_throughput_mbps *
+           1000  /
+           (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
+
+       /*
+        * Make sure the fill level is at least 1 less than the ring size.
+        * Leaving 1 element empty allows the SW to easily distinguish
+        * between a full ring vs. an empty ring.
+        */
+       if (size >= htt->rx_ring.size)
+               size = htt->rx_ring.size - 1;
+
+       return size;
+}
+
+static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct ath10k_skb_cb *cb;
+       int i;
+
+       for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
+               skb = htt->rx_ring.netbufs_ring[i];
+               cb = ATH10K_SKB_CB(skb);
+               dma_unmap_single(htt->ar->dev, cb->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+       }
+
+       htt->rx_ring.fill_cnt = 0;
+}
+
+static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+       struct htt_rx_desc *rx_desc;
+       struct sk_buff *skb;
+       dma_addr_t paddr;
+       int ret = 0, idx;
+
+       idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr));
+       while (num > 0) {
+               skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+               if (!skb) {
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN))
+                       skb_pull(skb,
+                                PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) -
+                                skb->data);
+
+               /* Clear rx_desc attention word before posting to Rx ring */
+               rx_desc = (struct htt_rx_desc *)skb->data;
+               rx_desc->attention.flags = __cpu_to_le32(0);
+
+               paddr = dma_map_single(htt->ar->dev, skb->data,
+                                      skb->len + skb_tailroom(skb),
+                                      DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) {
+                       dev_kfree_skb_any(skb);
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               ATH10K_SKB_CB(skb)->paddr = paddr;
+               htt->rx_ring.netbufs_ring[idx] = skb;
+               htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
+               htt->rx_ring.fill_cnt++;
+
+               num--;
+               idx++;
+               idx &= htt->rx_ring.size_mask;
+       }
+
+fail:
+       *(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx);
+       return ret;
+}
+
+static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+       lockdep_assert_held(&htt->rx_ring.lock);
+       return __ath10k_htt_rx_ring_fill_n(htt, num);
+}
+
+static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
+{
+       int ret, num_to_fill;
+
+       spin_lock_bh(&htt->rx_ring.lock);
+       num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+       ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
+       if (ret == -ENOMEM) {
+               /*
+                * Failed to fill it to the desired level -
+                * we'll start a timer and try again next time.
+                * As long as enough buffers are left in the ring for
+                * another A-MPDU rx, no special recovery is needed.
+                */
+               mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
+                         msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+       }
+       spin_unlock_bh(&htt->rx_ring.lock);
+}
+
+static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
+{
+       struct ath10k_htt *htt = (struct ath10k_htt *)arg;
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt)
+{
+       return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) -
+               htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask;
+}
+
+void ath10k_htt_rx_detach(struct ath10k_htt *htt)
+{
+       int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+
+       del_timer_sync(&htt->rx_ring.refill_retry_timer);
+
+       while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
+               struct sk_buff *skb =
+                               htt->rx_ring.netbufs_ring[sw_rd_idx];
+               struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+               dma_unmap_single(htt->ar->dev, cb->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]);
+               sw_rd_idx++;
+               sw_rd_idx &= htt->rx_ring.size_mask;
+       }
+
+       dma_free_coherent(htt->ar->dev,
+                         (htt->rx_ring.size *
+                          sizeof(htt->rx_ring.paddrs_ring)),
+                         htt->rx_ring.paddrs_ring,
+                         htt->rx_ring.base_paddr);
+
+       dma_free_coherent(htt->ar->dev,
+                         sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                         htt->rx_ring.alloc_idx.vaddr,
+                         htt->rx_ring.alloc_idx.paddr);
+
+       kfree(htt->rx_ring.netbufs_ring);
+}
+
+static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
+{
+       int idx;
+       struct sk_buff *msdu;
+
+       spin_lock_bh(&htt->rx_ring.lock);
+
+       if (ath10k_htt_rx_ring_elems(htt) == 0)
+               ath10k_warn("htt rx ring is empty!\n");
+
+       idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+       msdu = htt->rx_ring.netbufs_ring[idx];
+
+       idx++;
+       idx &= htt->rx_ring.size_mask;
+       htt->rx_ring.sw_rd_idx.msdu_payld = idx;
+       htt->rx_ring.fill_cnt--;
+
+       spin_unlock_bh(&htt->rx_ring.lock);
+       return msdu;
+}
+
+static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
+{
+       struct sk_buff *next;
+
+       while (skb) {
+               next = skb->next;
+               dev_kfree_skb_any(skb);
+               skb = next;
+       }
+}
+
+static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
+                                  u8 **fw_desc, int *fw_desc_len,
+                                  struct sk_buff **head_msdu,
+                                  struct sk_buff **tail_msdu)
+{
+       int msdu_len, msdu_chaining = 0;
+       struct sk_buff *msdu;
+       struct htt_rx_desc *rx_desc;
+
+       if (ath10k_htt_rx_ring_elems(htt) == 0)
+               ath10k_warn("htt rx ring is empty!\n");
+
+       if (htt->rx_confused) {
+               ath10k_warn("htt is confused. refusing rx\n");
+               return 0;
+       }
+
+       msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
+       while (msdu) {
+               int last_msdu, msdu_len_invalid, msdu_chained;
+
+               dma_unmap_single(htt->ar->dev,
+                                ATH10K_SKB_CB(msdu)->paddr,
+                                msdu->len + skb_tailroom(msdu),
+                                DMA_FROM_DEVICE);
+
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+                               msdu->data, msdu->len + skb_tailroom(msdu));
+
+               rx_desc = (struct htt_rx_desc *)msdu->data;
+
+               /* FIXME: we must report msdu payload since this is what caller
+                *        expects now */
+               skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+               skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+
+               /*
+                * Sanity check - confirm the HW is finished filling in the
+                * rx data.
+                * If the HW and SW are working correctly, then it's guaranteed
+                * that the HW's MAC DMA is done before this point in the SW.
+                * To prevent the case that we handle a stale Rx descriptor,
+                * just assert for now until we have a way to recover.
+                */
+               if (!(__le32_to_cpu(rx_desc->attention.flags)
+                               & RX_ATTENTION_FLAGS_MSDU_DONE)) {
+                       ath10k_htt_rx_free_msdu_chain(*head_msdu);
+                       *head_msdu = NULL;
+                       msdu = NULL;
+                       ath10k_err("htt rx stopped. cannot recover\n");
+                       htt->rx_confused = true;
+                       break;
+               }
+
+               /*
+                * Copy the FW rx descriptor for this MSDU from the rx
+                * indication message into the MSDU's netbuf. HL uses the
+                * same rx indication message definition as LL, and simply
+                * appends new info (fields from the HW rx desc, and the
+                * MSDU payload itself). So, the offset into the rx
+                * indication message only has to account for the standard
+                * offset of the per-MSDU FW rx desc info within the
+                * message, and how many bytes of the per-MSDU FW rx desc
+                * info have already been consumed. (And the endianness of
+                * the host, since for a big-endian host, the rx ind
+                * message contents, including the per-MSDU rx desc bytes,
+                * were byteswapped during upload.)
+                */
+               if (*fw_desc_len > 0) {
+                       rx_desc->fw_desc.info0 = **fw_desc;
+                       /*
+                        * The target is expected to only provide the basic
+                        * per-MSDU rx descriptors. Just to be sure, verify
+                        * that the target has not attached extension data
+                        * (e.g. LRO flow ID).
+                        */
+
+                       /* or more, if there's extension data */
+                       (*fw_desc)++;
+                       (*fw_desc_len)--;
+               } else {
+                       /*
+                        * When an oversized AMSDU happened, FW will lost
+                        * some of MSDU status - in this case, the FW
+                        * descriptors provided will be less than the
+                        * actual MSDUs inside this MPDU. Mark the FW
+                        * descriptors so that it will still deliver to
+                        * upper stack, if no CRC error for this MPDU.
+                        *
+                        * FIX THIS - the FW descriptors are actually for
+                        * MSDUs in the end of this A-MSDU instead of the
+                        * beginning.
+                        */
+                       rx_desc->fw_desc.info0 = 0;
+               }
+
+               msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags)
+                                       & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR |
+                                          RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR));
+               msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
+                             RX_MSDU_START_INFO0_MSDU_LENGTH);
+               msdu_chained = rx_desc->frag_info.ring2_more_count;
+
+               if (msdu_len_invalid)
+                       msdu_len = 0;
+
+               skb_trim(msdu, 0);
+               skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
+               msdu_len -= msdu->len;
+
+               /* FIXME: Do chained buffers include htt_rx_desc or not? */
+               while (msdu_chained--) {
+                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+
+                       dma_unmap_single(htt->ar->dev,
+                                        ATH10K_SKB_CB(next)->paddr,
+                                        next->len + skb_tailroom(next),
+                                        DMA_FROM_DEVICE);
+
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+                                       next->data,
+                                       next->len + skb_tailroom(next));
+
+                       skb_trim(next, 0);
+                       skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
+                       msdu_len -= next->len;
+
+                       msdu->next = next;
+                       msdu = next;
+                       msdu_chaining = 1;
+               }
+
+               if (msdu_len > 0) {
+                       /* This may suggest FW bug? */
+                       ath10k_warn("htt rx msdu len not consumed (%d)\n",
+                                   msdu_len);
+               }
+
+               last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
+                               RX_MSDU_END_INFO0_LAST_MSDU;
+
+               if (last_msdu) {
+                       msdu->next = NULL;
+                       break;
+               } else {
+                       struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+                       msdu->next = next;
+                       msdu = next;
+               }
+       }
+       *tail_msdu = msdu;
+
+       /*
+        * Don't refill the ring yet.
+        *
+        * First, the elements popped here are still in use - it is not
+        * safe to overwrite them until the matching call to
+        * mpdu_desc_list_next. Second, for efficiency it is preferable to
+        * refill the rx ring with 1 PPDU's worth of rx buffers (something
+        * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers
+        * (something like 3 buffers). Consequently, we'll rely on the txrx
+        * SW to tell us when it is done pulling all the PPDU's rx buffers
+        * out of the rx ring, and then refill it just once.
+        */
+
+       return msdu_chaining;
+}
+
+int ath10k_htt_rx_attach(struct ath10k_htt *htt)
+{
+       dma_addr_t paddr;
+       void *vaddr;
+       struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
+
+       htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+       if (!is_power_of_2(htt->rx_ring.size)) {
+               ath10k_warn("htt rx ring size is not power of 2\n");
+               return -EINVAL;
+       }
+
+       htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+
+       /*
+        * Set the initial value for the level to which the rx ring
+        * should be filled, based on the max throughput and the
+        * worst likely latency for the host to fill the rx ring
+        * with new buffers. In theory, this fill level can be
+        * dynamically adjusted from the initial value set here, to
+        * reflect the actual host latency rather than a
+        * conservative assumption about the host latency.
+        */
+       htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
+
+       htt->rx_ring.netbufs_ring =
+               kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
+                       GFP_KERNEL);
+       if (!htt->rx_ring.netbufs_ring)
+               goto err_netbuf;
+
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                  (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)),
+                  &paddr, GFP_DMA);
+       if (!vaddr)
+               goto err_dma_ring;
+
+       htt->rx_ring.paddrs_ring = vaddr;
+       htt->rx_ring.base_paddr = paddr;
+
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                                  sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                                  &paddr, GFP_DMA);
+       if (!vaddr)
+               goto err_dma_idx;
+
+       htt->rx_ring.alloc_idx.vaddr = vaddr;
+       htt->rx_ring.alloc_idx.paddr = paddr;
+       htt->rx_ring.sw_rd_idx.msdu_payld = 0;
+       *htt->rx_ring.alloc_idx.vaddr = 0;
+
+       /* Initialize the Rx refill retry timer */
+       setup_timer(timer, ath10k_htt_rx_ring_refill_retry, (unsigned long)htt);
+
+       spin_lock_init(&htt->rx_ring.lock);
+
+       htt->rx_ring.fill_cnt = 0;
+       if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
+               goto err_fill_ring;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+                  htt->rx_ring.size, htt->rx_ring.fill_level);
+       return 0;
+
+err_fill_ring:
+       ath10k_htt_rx_ring_free(htt);
+       dma_free_coherent(htt->ar->dev,
+                         sizeof(*htt->rx_ring.alloc_idx.vaddr),
+                         htt->rx_ring.alloc_idx.vaddr,
+                         htt->rx_ring.alloc_idx.paddr);
+err_dma_idx:
+       dma_free_coherent(htt->ar->dev,
+                         (htt->rx_ring.size *
+                          sizeof(htt->rx_ring.paddrs_ring)),
+                         htt->rx_ring.paddrs_ring,
+                         htt->rx_ring.base_paddr);
+err_dma_ring:
+       kfree(htt->rx_ring.netbufs_ring);
+err_netbuf:
+       return -ENOMEM;
+}
+
+static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type)
+{
+       switch (type) {
+       case HTT_RX_MPDU_ENCRYPT_WEP40:
+       case HTT_RX_MPDU_ENCRYPT_WEP104:
+               return 4;
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+       case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+       case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
+       case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+               return 8;
+       case HTT_RX_MPDU_ENCRYPT_NONE:
+               return 0;
+       }
+
+       ath10k_warn("unknown encryption type %d\n", type);
+       return 0;
+}
+
+static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type)
+{
+       switch (type) {
+       case HTT_RX_MPDU_ENCRYPT_NONE:
+       case HTT_RX_MPDU_ENCRYPT_WEP40:
+       case HTT_RX_MPDU_ENCRYPT_WEP104:
+       case HTT_RX_MPDU_ENCRYPT_WEP128:
+       case HTT_RX_MPDU_ENCRYPT_WAPI:
+               return 0;
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+       case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+               return 4;
+       case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+               return 8;
+       }
+
+       ath10k_warn("unknown encryption type %d\n", type);
+       return 0;
+}
+
+/* Applies for first msdu in chain, before altering it. */
+static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format fmt;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       if (fmt == RX_MSDU_DECAP_RAW)
+               return (void *)skb->data;
+       else
+               return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+}
+
+/* This function only applies for first msdu in an msdu chain */
+static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
+{
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               if (qc[0] & 0x80)
+                       return true;
+       }
+       return false;
+}
+
+static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+                       struct htt_rx_info *info)
+{
+       struct htt_rx_desc *rxd;
+       struct sk_buff *amsdu;
+       struct sk_buff *first;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb = info->skb;
+       enum rx_msdu_decap_format fmt;
+       enum htt_rx_mpdu_encrypt_type enctype;
+       unsigned int hdr_len;
+       int crypto_len;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                       RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+       /* FIXME: No idea what assumptions are safe here. Need logs */
+       if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
+           (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+               return -ENOTSUPP;
+       }
+
+       /* A-MSDU max is a little less than 8K */
+       amsdu = dev_alloc_skb(8*1024);
+       if (!amsdu) {
+               ath10k_warn("A-MSDU allocation failed\n");
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+               return -ENOMEM;
+       }
+
+       if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
+               int hdrlen;
+
+               hdr = (void *)rxd->rx_hdr_status;
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
+       }
+
+       first = skb;
+       while (skb) {
+               void *decap_hdr;
+               int decap_len = 0;
+
+               rxd = (void *)skb->data - sizeof(*rxd);
+               fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                               RX_MSDU_START_INFO1_DECAP_FORMAT);
+               decap_hdr = (void *)rxd->rx_hdr_status;
+
+               if (skb == first) {
+                       /* We receive linked A-MSDU subframe skbuffs. The
+                        * first one contains the original 802.11 header (and
+                        * possible crypto param) in the RX descriptor. The
+                        * A-MSDU subframe header follows that. Each part is
+                        * aligned to 4 byte boundary. */
+
+                       hdr = (void *)amsdu->data;
+                       hdr_len = ieee80211_hdrlen(hdr->frame_control);
+                       crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
+
+                       decap_hdr += roundup(hdr_len, 4);
+                       decap_hdr += roundup(crypto_len, 4);
+               }
+
+               if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+                       /* Ethernet2 decap inserts ethernet header in place of
+                        * A-MSDU subframe header. */
+                       skb_pull(skb, 6 + 6 + 2);
+
+                       /* A-MSDU subframe header length */
+                       decap_len += 6 + 6 + 2;
+
+                       /* Ethernet2 decap also strips the LLC/SNAP so we need
+                        * to re-insert it. The LLC/SNAP follows A-MSDU
+                        * subframe header. */
+                       /* FIXME: Not all LLCs are 8 bytes long */
+                       decap_len += 8;
+
+                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               }
+
+               if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
+                       /* Native Wifi decap inserts regular 802.11 header
+                        * in place of A-MSDU subframe header. */
+                       hdr = (struct ieee80211_hdr *)skb->data;
+                       skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
+
+                       /* A-MSDU subframe header length */
+                       decap_len += 6 + 6 + 2;
+
+                       memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+               }
+
+               if (fmt == RX_MSDU_DECAP_RAW)
+                       skb_trim(skb, skb->len - 4); /* remove FCS */
+
+               memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+
+               /* A-MSDU subframes are padded to 4bytes
+                * but relative to first subframe, not the whole MPDU */
+               if (skb->next && ((decap_len + skb->len) & 3)) {
+                       int padlen = 4 - ((decap_len + skb->len) & 3);
+                       memset(skb_put(amsdu, padlen), 0, padlen);
+               }
+
+               skb = skb->next;
+       }
+
+       info->skb = amsdu;
+       info->encrypt_type = enctype;
+
+       ath10k_htt_rx_free_msdu_chain(first);
+
+       return 0;
+}
+
+static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+{
+       struct sk_buff *skb = info->skb;
+       struct htt_rx_desc *rxd;
+       struct ieee80211_hdr *hdr;
+       enum rx_msdu_decap_format fmt;
+       enum htt_rx_mpdu_encrypt_type enctype;
+
+       /* This shouldn't happen. If it does than it may be a FW bug. */
+       if (skb->next) {
+               ath10k_warn("received chained non A-MSDU frame\n");
+               ath10k_htt_rx_free_msdu_chain(skb->next);
+               skb->next = NULL;
+       }
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                       RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+
+       switch (fmt) {
+       case RX_MSDU_DECAP_RAW:
+               /* remove trailing FCS */
+               skb_trim(skb, skb->len - 4);
+               break;
+       case RX_MSDU_DECAP_NATIVE_WIFI:
+               /* nothing to do here */
+               break;
+       case RX_MSDU_DECAP_ETHERNET2_DIX:
+               /* macaddr[6] + macaddr[6] + ethertype[2] */
+               skb_pull(skb, 6 + 6 + 2);
+               break;
+       case RX_MSDU_DECAP_8023_SNAP_LLC:
+               /* macaddr[6] + macaddr[6] + len[2] */
+               /* we don't need this for non-A-MSDU */
+               skb_pull(skb, 6 + 6 + 2);
+               break;
+       }
+
+       if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+               void *llc;
+               int llclen;
+
+               llclen = 8;
+               llc  = hdr;
+               llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
+               llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+
+               skb_push(skb, llclen);
+               memcpy(skb->data, llc, llclen);
+       }
+
+       if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
+               int len = ieee80211_hdrlen(hdr->frame_control);
+               skb_push(skb, len);
+               memcpy(skb->data, hdr, len);
+       }
+
+       info->skb = skb;
+       info->encrypt_type = enctype;
+       return 0;
+}
+
+static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR)
+               return true;
+
+       return false;
+}
+
+static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_FCS_ERR)
+               return true;
+
+       return false;
+}
+
+static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
+                                 struct htt_rx_indication *rx)
+{
+       struct htt_rx_info info;
+       struct htt_rx_indication_mpdu_range *mpdu_ranges;
+       struct ieee80211_hdr *hdr;
+       int num_mpdu_ranges;
+       int fw_desc_len;
+       u8 *fw_desc;
+       int i, j;
+       int ret;
+
+       memset(&info, 0, sizeof(info));
+
+       fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
+       fw_desc = (u8 *)&rx->fw_desc;
+
+       num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
+                            HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+       mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
+
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
+                       rx, sizeof(*rx) +
+                       (sizeof(struct htt_rx_indication_mpdu_range) *
+                               num_mpdu_ranges));
+
+       for (i = 0; i < num_mpdu_ranges; i++) {
+               info.status = mpdu_ranges[i].mpdu_range_status;
+
+               for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
+                       struct sk_buff *msdu_head, *msdu_tail;
+                       enum htt_rx_mpdu_status status;
+                       int msdu_chaining;
+
+                       msdu_head = NULL;
+                       msdu_tail = NULL;
+                       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt,
+                                                        &fw_desc,
+                                                        &fw_desc_len,
+                                                        &msdu_head,
+                                                        &msdu_tail);
+
+                       if (!msdu_head) {
+                               ath10k_warn("htt rx no data!\n");
+                               continue;
+                       }
+
+                       if (msdu_head->len == 0) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx dropping due to zero-len\n");
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       status = info.status;
+
+                       /* Skip mgmt frames while we handle this in WMI */
+                       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+                           status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+                           !htt->ar->monitor_enabled) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx ignoring frame w/ status %d\n",
+                                          status);
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       /* FIXME: we do not support chaining yet.
+                        * this needs investigation */
+                       if (msdu_chaining) {
+                               ath10k_warn("msdu_chaining is true\n");
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
+                       info.skb     = msdu_head;
+                       info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
+                       info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
+                       info.signal += rx->ppdu.combined_rssi;
+
+                       info.rate.info0 = rx->ppdu.info0;
+                       info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
+                       info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
+
+                       hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
+
+                       if (ath10k_htt_rx_hdr_is_amsdu(hdr))
+                               ret = ath10k_htt_rx_amsdu(htt, &info);
+                       else
+                               ret = ath10k_htt_rx_msdu(htt, &info);
+
+                       if (ret && !info.fcs_err) {
+                               ath10k_warn("error processing msdus %d\n", ret);
+                               dev_kfree_skb_any(info.skb);
+                               continue;
+                       }
+
+                       if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
+                               ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
+
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
+                                       info.skb->data, info.skb->len);
+                       ath10k_process_rx(htt->ar, &info);
+               }
+       }
+
+       ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
+                               struct htt_rx_fragment_indication *frag)
+{
+       struct sk_buff *msdu_head, *msdu_tail;
+       struct htt_rx_desc *rxd;
+       enum rx_msdu_decap_format fmt;
+       struct htt_rx_info info = {};
+       struct ieee80211_hdr *hdr;
+       int msdu_chaining;
+       bool tkip_mic_err;
+       bool decrypt_err;
+       u8 *fw_desc;
+       int fw_desc_len, hdrlen, paramlen;
+       int trim;
+
+       fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
+       fw_desc = (u8 *)frag->fw_msdu_rx_desc;
+
+       msdu_head = NULL;
+       msdu_tail = NULL;
+       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+                                               &msdu_head, &msdu_tail);
+
+       ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
+
+       if (!msdu_head) {
+               ath10k_warn("htt rx frag no data\n");
+               return;
+       }
+
+       if (msdu_chaining || msdu_head != msdu_tail) {
+               ath10k_warn("aggregation with fragmentation?!\n");
+               ath10k_htt_rx_free_msdu_chain(msdu_head);
+               return;
+       }
+
+       /* FIXME: implement signal strength */
+
+       hdr = (struct ieee80211_hdr *)msdu_head->data;
+       rxd = (void *)msdu_head->data - sizeof(*rxd);
+       tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) &
+                               RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+       decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) &
+                               RX_ATTENTION_FLAGS_DECRYPT_ERR);
+       fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+                       RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+       if (fmt != RX_MSDU_DECAP_RAW) {
+               ath10k_warn("we dont support non-raw fragmented rx yet\n");
+               dev_kfree_skb_any(msdu_head);
+               goto end;
+       }
+
+       info.skb = msdu_head;
+       info.status = HTT_RX_IND_MPDU_STATUS_OK;
+       info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                               RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+       if (tkip_mic_err) {
+               ath10k_warn("tkip mic error\n");
+               info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR;
+       }
+
+       if (decrypt_err) {
+               ath10k_warn("decryption err in fragmented rx\n");
+               dev_kfree_skb_any(info.skb);
+               goto end;
+       }
+
+       if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+               hdrlen = ieee80211_hdrlen(hdr->frame_control);
+               paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type);
+
+               /* It is more efficient to move the header than the payload */
+               memmove((void *)info.skb->data + paramlen,
+                       (void *)info.skb->data,
+                       hdrlen);
+               skb_pull(info.skb, paramlen);
+               hdr = (struct ieee80211_hdr *)info.skb->data;
+       }
+
+       /* remove trailing FCS */
+       trim  = 4;
+
+       /* remove crypto trailer */
+       trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type);
+
+       /* last fragment of TKIP frags has MIC */
+       if (!ieee80211_has_morefrags(hdr->frame_control) &&
+           info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+               trim += 8;
+
+       if (trim > info.skb->len) {
+               ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
+               dev_kfree_skb_any(info.skb);
+               goto end;
+       }
+
+       skb_trim(info.skb, info.skb->len - trim);
+
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+                       info.skb->data, info.skb->len);
+       ath10k_process_rx(htt->ar, &info);
+
+end:
+       if (fw_desc_len > 0) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "expecting more fragmented rx in one indication %d\n",
+                          fw_desc_len);
+       }
+}
+
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ath10k_htt *htt = ar->htt;
+       struct htt_resp *resp = (struct htt_resp *)skb->data;
+
+       /* confirm alignment */
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("unaligned htt message, expect trouble\n");
+
+       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+                  resp->hdr.msg_type);
+       switch (resp->hdr.msg_type) {
+       case HTT_T2H_MSG_TYPE_VERSION_CONF: {
+               htt->target_version_major = resp->ver_resp.major;
+               htt->target_version_minor = resp->ver_resp.minor;
+               complete(&htt->target_version_received);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_RX_IND: {
+               ath10k_htt_rx_handler(htt, &resp->rx_ind);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_PEER_MAP: {
+               struct htt_peer_map_event ev = {
+                       .vdev_id = resp->peer_map.vdev_id,
+                       .peer_id = __le16_to_cpu(resp->peer_map.peer_id),
+               };
+               memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr));
+               ath10k_peer_map_event(htt, &ev);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_PEER_UNMAP: {
+               struct htt_peer_unmap_event ev = {
+                       .peer_id = __le16_to_cpu(resp->peer_unmap.peer_id),
+               };
+               ath10k_peer_unmap_event(htt, &ev);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
+               struct htt_tx_done tx_done = {};
+               int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
+
+               tx_done.msdu_id =
+                       __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
+
+               switch (status) {
+               case HTT_MGMT_TX_STATUS_OK:
+                       break;
+               case HTT_MGMT_TX_STATUS_RETRY:
+                       tx_done.no_ack = true;
+                       break;
+               case HTT_MGMT_TX_STATUS_DROP:
+                       tx_done.discard = true;
+                       break;
+               }
+
+               ath10k_txrx_tx_completed(htt, &tx_done);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
+               struct htt_tx_done tx_done = {};
+               int status = MS(resp->data_tx_completion.flags,
+                               HTT_DATA_TX_STATUS);
+               __le16 msdu_id;
+               int i;
+
+               switch (status) {
+               case HTT_DATA_TX_STATUS_NO_ACK:
+                       tx_done.no_ack = true;
+                       break;
+               case HTT_DATA_TX_STATUS_OK:
+                       break;
+               case HTT_DATA_TX_STATUS_DISCARD:
+               case HTT_DATA_TX_STATUS_POSTPONE:
+               case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
+                       tx_done.discard = true;
+                       break;
+               default:
+                       ath10k_warn("unhandled tx completion status %d\n",
+                                   status);
+                       tx_done.discard = true;
+                       break;
+               }
+
+               ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+                          resp->data_tx_completion.num_msdus);
+
+               for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+                       msdu_id = resp->data_tx_completion.msdus[i];
+                       tx_done.msdu_id = __le16_to_cpu(msdu_id);
+                       ath10k_txrx_tx_completed(htt, &tx_done);
+               }
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_SEC_IND: {
+               struct ath10k *ar = htt->ar;
+               struct htt_security_indication *ev = &resp->security_indication;
+
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "sec ind peer_id %d unicast %d type %d\n",
+                         __le16_to_cpu(ev->peer_id),
+                         !!(ev->flags & HTT_SECURITY_IS_UNICAST),
+                         MS(ev->flags, HTT_SECURITY_TYPE));
+               complete(&ar->install_key_done);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+                               skb->data, skb->len);
+               ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind);
+               break;
+       }
+       case HTT_T2H_MSG_TYPE_TEST:
+               /* FIX THIS */
+               break;
+       case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
+       case HTT_T2H_MSG_TYPE_STATS_CONF:
+       case HTT_T2H_MSG_TYPE_RX_ADDBA:
+       case HTT_T2H_MSG_TYPE_RX_DELBA:
+       case HTT_T2H_MSG_TYPE_RX_FLUSH:
+       default:
+               ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n",
+                          resp->hdr.msg_type);
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+                               skb->data, skb->len);
+               break;
+       };
+
+       /* Free the indication buffer */
+       dev_kfree_skb_any(skb);
+}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
new file mode 100644 (file)
index 0000000..ef79106
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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/etherdevice.h>
+#include "htt.h"
+#include "mac.h"
+#include "hif.h"
+#include "txrx.h"
+#include "debug.h"
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       htt->num_pending_tx--;
+       if (htt->num_pending_tx == htt->max_num_pending_tx - 1)
+               ieee80211_wake_queues(htt->ar->hw);
+}
+
+static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       spin_lock_bh(&htt->tx_lock);
+       __ath10k_htt_tx_dec_pending(htt);
+       spin_unlock_bh(&htt->tx_lock);
+}
+
+static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
+{
+       int ret = 0;
+
+       spin_lock_bh(&htt->tx_lock);
+
+       if (htt->num_pending_tx >= htt->max_num_pending_tx) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       htt->num_pending_tx++;
+       if (htt->num_pending_tx == htt->max_num_pending_tx)
+               ieee80211_stop_queues(htt->ar->hw);
+
+exit:
+       spin_unlock_bh(&htt->tx_lock);
+       return ret;
+}
+
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
+{
+       int msdu_id;
+
+       lockdep_assert_held(&htt->tx_lock);
+
+       msdu_id = find_first_zero_bit(htt->used_msdu_ids,
+                                     htt->max_num_pending_tx);
+       if (msdu_id == htt->max_num_pending_tx)
+               return -ENOBUFS;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
+       __set_bit(msdu_id, htt->used_msdu_ids);
+       return msdu_id;
+}
+
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
+{
+       lockdep_assert_held(&htt->tx_lock);
+
+       if (!test_bit(msdu_id, htt->used_msdu_ids))
+               ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id);
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
+       __clear_bit(msdu_id, htt->used_msdu_ids);
+}
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt)
+{
+       u8 pipe;
+
+       spin_lock_init(&htt->tx_lock);
+       init_waitqueue_head(&htt->empty_tx_wq);
+
+       /* At the beginning free queue number should hint us the maximum
+        * queue length */
+       pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+       htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
+                                                                  pipe);
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
+                  htt->max_num_pending_tx);
+
+       htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
+                                 htt->max_num_pending_tx, GFP_KERNEL);
+       if (!htt->pending_tx)
+               return -ENOMEM;
+
+       htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
+                                    BITS_TO_LONGS(htt->max_num_pending_tx),
+                                    GFP_KERNEL);
+       if (!htt->used_msdu_ids) {
+               kfree(htt->pending_tx);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
+{
+       struct sk_buff *txdesc;
+       int msdu_id;
+
+       /* No locks needed. Called after communication with the device has
+        * been stopped. */
+
+       for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
+               if (!test_bit(msdu_id, htt->used_msdu_ids))
+                       continue;
+
+               txdesc = htt->pending_tx[msdu_id];
+               if (!txdesc)
+                       continue;
+
+               ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
+                          msdu_id);
+
+               if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+                       ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
+
+               ATH10K_SKB_CB(txdesc)->htt.discard = true;
+               ath10k_txrx_tx_unref(htt, txdesc);
+       }
+}
+
+void ath10k_htt_tx_detach(struct ath10k_htt *htt)
+{
+       ath10k_htt_tx_cleanup_pending(htt);
+       kfree(htt->pending_tx);
+       kfree(htt->used_msdu_ids);
+       return;
+}
+
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       struct ath10k_htt *htt = ar->htt;
+
+       if (skb_cb->htt.is_conf) {
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       if (skb_cb->is_aborted) {
+               skb_cb->htt.discard = true;
+
+               /* if the skbuff is aborted we need to make sure we'll free up
+                * the tx resources, we can't simply run tx_unref() 2 times
+                * because if htt tx completion came in earlier we'd access
+                * unallocated memory */
+               if (skb_cb->htt.refcount > 1)
+                       skb_cb->htt.refcount = 1;
+       }
+
+       ath10k_txrx_tx_unref(htt, skb);
+}
+
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       int len = 0;
+       int ret;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->ver_req);
+
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
+
+       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
+{
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       struct htt_rx_ring_setup_ring *ring;
+       const int num_rx_ring = 1;
+       u16 flags;
+       u32 fw_idx;
+       int len;
+       int ret;
+
+       /*
+        * the HW expects the buffer to be an integral number of 4-byte
+        * "words"
+        */
+       BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4));
+       BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0);
+
+       len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr)
+           + (sizeof(*ring) * num_rx_ring);
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+
+       cmd = (struct htt_cmd *)skb->data;
+       ring = &cmd->rx_setup.rings[0];
+
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG;
+       cmd->rx_setup.hdr.num_rings = 1;
+
+       /* FIXME: do we need all of this? */
+       flags = 0;
+       flags |= HTT_RX_RING_FLAGS_MAC80211_HDR;
+       flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD;
+       flags |= HTT_RX_RING_FLAGS_PPDU_START;
+       flags |= HTT_RX_RING_FLAGS_PPDU_END;
+       flags |= HTT_RX_RING_FLAGS_MPDU_START;
+       flags |= HTT_RX_RING_FLAGS_MPDU_END;
+       flags |= HTT_RX_RING_FLAGS_MSDU_START;
+       flags |= HTT_RX_RING_FLAGS_MSDU_END;
+       flags |= HTT_RX_RING_FLAGS_RX_ATTENTION;
+       flags |= HTT_RX_RING_FLAGS_FRAG_INFO;
+       flags |= HTT_RX_RING_FLAGS_UNICAST_RX;
+       flags |= HTT_RX_RING_FLAGS_MULTICAST_RX;
+       flags |= HTT_RX_RING_FLAGS_CTRL_RX;
+       flags |= HTT_RX_RING_FLAGS_MGMT_RX;
+       flags |= HTT_RX_RING_FLAGS_NULL_RX;
+       flags |= HTT_RX_RING_FLAGS_PHY_DATA_RX;
+
+       fw_idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
+
+       ring->fw_idx_shadow_reg_paddr =
+               __cpu_to_le32(htt->rx_ring.alloc_idx.paddr);
+       ring->rx_ring_base_paddr = __cpu_to_le32(htt->rx_ring.base_paddr);
+       ring->rx_ring_len = __cpu_to_le16(htt->rx_ring.size);
+       ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE);
+       ring->flags = __cpu_to_le16(flags);
+       ring->fw_idx_init_val = __cpu_to_le16(fw_idx);
+
+#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4)
+
+       ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status));
+       ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload));
+       ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start));
+       ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end));
+       ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start));
+       ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end));
+       ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start));
+       ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end));
+       ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention));
+       ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info));
+
+#undef desc_offset
+
+       ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+       ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+       struct device *dev = htt->ar->dev;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *txdesc = NULL;
+       struct htt_cmd *cmd;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       int len = 0;
+       int msdu_id = -1;
+       int res;
+
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
+       len += sizeof(cmd->hdr);
+       len += sizeof(cmd->mgmt_tx);
+
+       txdesc = ath10k_htc_alloc_skb(len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       spin_lock_bh(&htt->tx_lock);
+       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (msdu_id < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               res = msdu_id;
+               goto err;
+       }
+       htt->pending_tx[msdu_id] = txdesc;
+       spin_unlock_bh(&htt->tx_lock);
+
+       res = ath10k_skb_map(dev, msdu);
+       if (res)
+               goto err;
+
+       skb_put(txdesc, len);
+       cmd = (struct htt_cmd *)txdesc->data;
+       cmd->hdr.msg_type         = HTT_H2T_MSG_TYPE_MGMT_TX;
+       cmd->mgmt_tx.msdu_paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+       cmd->mgmt_tx.len        = __cpu_to_le32(msdu->len);
+       cmd->mgmt_tx.desc_id    = __cpu_to_le32(msdu_id);
+       cmd->mgmt_tx.vdev_id    = __cpu_to_le32(vdev_id);
+       memcpy(cmd->mgmt_tx.hdr, msdu->data,
+              min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
+
+       /* refcount is decremented by HTC and HTT completions until it reaches
+        * zero and is freed */
+       skb_cb = ATH10K_SKB_CB(txdesc);
+       skb_cb->htt.msdu_id = msdu_id;
+       skb_cb->htt.refcount = 2;
+       skb_cb->htt.msdu = msdu;
+
+       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       if (res)
+               goto err;
+
+       return 0;
+
+err:
+       ath10k_skb_unmap(dev, msdu);
+
+       if (txdesc)
+               dev_kfree_skb_any(txdesc);
+       if (msdu_id >= 0) {
+               spin_lock_bh(&htt->tx_lock);
+               htt->pending_tx[msdu_id] = NULL;
+               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+               spin_unlock_bh(&htt->tx_lock);
+       }
+       ath10k_htt_tx_dec_pending(htt);
+       return res;
+}
+
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+       struct device *dev = htt->ar->dev;
+       struct htt_cmd *cmd;
+       struct htt_data_tx_desc_frag *tx_frags;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+       struct ath10k_skb_cb *skb_cb;
+       struct sk_buff *txdesc = NULL;
+       struct sk_buff *txfrag = NULL;
+       u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+       u8 tid;
+       int prefetch_len, desc_len, frag_len;
+       dma_addr_t frags_paddr;
+       int msdu_id = -1;
+       int res;
+       u8 flags0;
+       u16 flags1;
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
+       prefetch_len = min(htt->prefetch_len, msdu->len);
+       prefetch_len = roundup(prefetch_len, 4);
+
+       desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
+       frag_len = sizeof(*tx_frags) * 2;
+
+       txdesc = ath10k_htc_alloc_skb(desc_len);
+       if (!txdesc) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       txfrag = dev_alloc_skb(frag_len);
+       if (!txfrag) {
+               res = -ENOMEM;
+               goto err;
+       }
+
+       if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
+               ath10k_warn("htt alignment check failed. dropping packet.\n");
+               res = -EIO;
+               goto err;
+       }
+
+       spin_lock_bh(&htt->tx_lock);
+       msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+       if (msdu_id < 0) {
+               spin_unlock_bh(&htt->tx_lock);
+               res = msdu_id;
+               goto err;
+       }
+       htt->pending_tx[msdu_id] = txdesc;
+       spin_unlock_bh(&htt->tx_lock);
+
+       res = ath10k_skb_map(dev, msdu);
+       if (res)
+               goto err;
+
+       /* tx fragment list must be terminated with zero-entry */
+       skb_put(txfrag, frag_len);
+       tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
+       tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+       tx_frags[0].len   = __cpu_to_le32(msdu->len);
+       tx_frags[1].paddr = __cpu_to_le32(0);
+       tx_frags[1].len   = __cpu_to_le32(0);
+
+       res = ath10k_skb_map(dev, txfrag);
+       if (res)
+               goto err;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n",
+                  (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr,
+                  (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
+                       txfrag->data, frag_len);
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
+                       msdu->data, msdu->len);
+
+       skb_put(txdesc, desc_len);
+       cmd = (struct htt_cmd *)txdesc->data;
+       memset(cmd, 0, desc_len);
+
+       tid = ATH10K_SKB_CB(msdu)->htt.tid;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid);
+
+       flags0  = 0;
+       if (!ieee80211_has_protected(hdr->frame_control))
+               flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+       flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
+       flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+                    HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+
+       flags1  = 0;
+       flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
+       flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
+
+       frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
+
+       cmd->hdr.msg_type        = HTT_H2T_MSG_TYPE_TX_FRM;
+       cmd->data_tx.flags0      = flags0;
+       cmd->data_tx.flags1      = __cpu_to_le16(flags1);
+       cmd->data_tx.len         = __cpu_to_le16(msdu->len);
+       cmd->data_tx.id          = __cpu_to_le16(msdu_id);
+       cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+       cmd->data_tx.peerid      = __cpu_to_le32(HTT_INVALID_PEERID);
+
+       memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
+
+       /* refcount is decremented by HTC and HTT completions until it reaches
+        * zero and is freed */
+       skb_cb = ATH10K_SKB_CB(txdesc);
+       skb_cb->htt.msdu_id = msdu_id;
+       skb_cb->htt.refcount = 2;
+       skb_cb->htt.txfrag = txfrag;
+       skb_cb->htt.msdu = msdu;
+
+       res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+       if (res)
+               goto err;
+
+       return 0;
+err:
+       if (txfrag)
+               ath10k_skb_unmap(dev, txfrag);
+       if (txdesc)
+               dev_kfree_skb_any(txdesc);
+       if (txfrag)
+               dev_kfree_skb_any(txfrag);
+       if (msdu_id >= 0) {
+               spin_lock_bh(&htt->tx_lock);
+               htt->pending_tx[msdu_id] = NULL;
+               ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+               spin_unlock_bh(&htt->tx_lock);
+       }
+       ath10k_htt_tx_dec_pending(htt);
+       ath10k_skb_unmap(dev, msdu);
+       return res;
+}
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
new file mode 100644 (file)
index 0000000..44ed5af
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _HW_H_
+#define _HW_H_
+
+#include "targaddrs.h"
+
+/* Supported FW version */
+#define SUPPORTED_FW_MAJOR     1
+#define SUPPORTED_FW_MINOR     0
+#define SUPPORTED_FW_RELEASE   0
+#define SUPPORTED_FW_BUILD     629
+
+/* QCA988X 1.0 definitions */
+#define QCA988X_HW_1_0_VERSION         0x4000002c
+#define QCA988X_HW_1_0_FW_DIR          "ath10k/QCA988X/hw1.0"
+#define QCA988X_HW_1_0_FW_FILE         "firmware.bin"
+#define QCA988X_HW_1_0_OTP_FILE                "otp.bin"
+#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin"
+#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234
+
+/* QCA988X 2.0 definitions */
+#define QCA988X_HW_2_0_VERSION         0x4100016c
+#define QCA988X_HW_2_0_FW_DIR          "ath10k/QCA988X/hw2.0"
+#define QCA988X_HW_2_0_FW_FILE         "firmware.bin"
+#define QCA988X_HW_2_0_OTP_FILE                "otp.bin"
+#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
+#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
+
+/* Known pecularities:
+ *  - current FW doesn't support raw rx mode (last tested v599)
+ *  - current FW dumps upon raw tx mode (last tested v599)
+ *  - raw appears in nwifi decap, raw and nwifi appear in ethernet decap
+ *  - raw have FCS, nwifi doesn't
+ *  - ethernet frames have 802.11 header decapped and parts (base hdr, cipher
+ *    param, llc/snap) are aligned to 4byte boundaries each */
+enum ath10k_hw_txrx_mode {
+       ATH10K_HW_TXRX_RAW = 0,
+       ATH10K_HW_TXRX_NATIVE_WIFI = 1,
+       ATH10K_HW_TXRX_ETHERNET = 2,
+};
+
+enum ath10k_mcast2ucast_mode {
+       ATH10K_MCAST2UCAST_DISABLED = 0,
+       ATH10K_MCAST2UCAST_ENABLED = 1,
+};
+
+#define TARGET_NUM_VDEVS                       8
+#define TARGET_NUM_PEER_AST                    2
+#define TARGET_NUM_WDS_ENTRIES                 32
+#define TARGET_DMA_BURST_SIZE                  0
+#define TARGET_MAC_AGGR_DELIM                  0
+#define TARGET_AST_SKID_LIMIT                  16
+#define TARGET_NUM_PEERS                       16
+#define TARGET_NUM_OFFLOAD_PEERS               0
+#define TARGET_NUM_OFFLOAD_REORDER_BUFS         0
+#define TARGET_NUM_PEER_KEYS                   2
+#define TARGET_NUM_TIDS                (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_TX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_CHAIN_MASK                   (BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_TIMEOUT_LO_PRI               100
+#define TARGET_RX_TIMEOUT_HI_PRI               40
+#define TARGET_RX_DECAP_MODE                   ATH10K_HW_TXRX_ETHERNET
+#define TARGET_SCAN_MAX_PENDING_REQS           4
+#define TARGET_BMISS_OFFLOAD_MAX_VDEV          3
+#define TARGET_ROAM_OFFLOAD_MAX_VDEV           3
+#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES    8
+#define TARGET_GTK_OFFLOAD_MAX_VDEV            3
+#define TARGET_NUM_MCAST_GROUPS                        0
+#define TARGET_NUM_MCAST_TABLE_ELEMS           0
+#define TARGET_MCAST2UCAST_MODE                        ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_TX_DBG_LOG_SIZE                 1024
+#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0
+#define TARGET_VOW_CONFIG                      0
+#define TARGET_NUM_MSDU_DESC                   (1024 + 400)
+#define TARGET_MAX_FRAG_ENTRIES                        0
+
+
+/* Number of Copy Engines supported */
+#define CE_COUNT 8
+
+/*
+ * Total number of PCIe MSI interrupts requested for all interrupt sources.
+ * PCIe standard forces this to be a power of 2.
+ * Some Host OS's limit MSI requests that can be granted to 8
+ * so for now we abide by this limit and avoid requesting more
+ * than that.
+ */
+#define MSI_NUM_REQUEST_LOG2   3
+#define MSI_NUM_REQUEST                (1<<MSI_NUM_REQUEST_LOG2)
+
+/*
+ * Granted MSIs are assigned as follows:
+ * Firmware uses the first
+ * Remaining MSIs, if any, are used by Copy Engines
+ * This mapping is known to both Target firmware and Host software.
+ * It may be changed as long as Host and Target are kept in sync.
+ */
+/* MSI for firmware (errors, etc.) */
+#define MSI_ASSIGN_FW          0
+
+/* MSIs for Copy Engines */
+#define MSI_ASSIGN_CE_INITIAL  1
+#define MSI_ASSIGN_CE_MAX      7
+
+/* as of IP3.7.1 */
+#define RTC_STATE_V_ON                         3
+
+#define RTC_STATE_COLD_RESET_MASK              0x00000400
+#define RTC_STATE_V_LSB                                0
+#define RTC_STATE_V_MASK                       0x00000007
+#define RTC_STATE_ADDRESS                      0x0000
+#define PCIE_SOC_WAKE_V_MASK                   0x00000001
+#define PCIE_SOC_WAKE_ADDRESS                  0x0004
+#define PCIE_SOC_WAKE_RESET                    0x00000000
+#define SOC_GLOBAL_RESET_ADDRESS               0x0008
+
+#define RTC_SOC_BASE_ADDRESS                   0x00004000
+#define RTC_WMAC_BASE_ADDRESS                  0x00005000
+#define MAC_COEX_BASE_ADDRESS                  0x00006000
+#define BT_COEX_BASE_ADDRESS                   0x00007000
+#define SOC_PCIE_BASE_ADDRESS                  0x00008000
+#define SOC_CORE_BASE_ADDRESS                  0x00009000
+#define WLAN_UART_BASE_ADDRESS                 0x0000c000
+#define WLAN_SI_BASE_ADDRESS                   0x00010000
+#define WLAN_GPIO_BASE_ADDRESS                 0x00014000
+#define WLAN_ANALOG_INTF_BASE_ADDRESS          0x0001c000
+#define WLAN_MAC_BASE_ADDRESS                  0x00020000
+#define EFUSE_BASE_ADDRESS                     0x00030000
+#define FPGA_REG_BASE_ADDRESS                  0x00039000
+#define WLAN_UART2_BASE_ADDRESS                        0x00054c00
+#define CE_WRAPPER_BASE_ADDRESS                        0x00057000
+#define CE0_BASE_ADDRESS                       0x00057400
+#define CE1_BASE_ADDRESS                       0x00057800
+#define CE2_BASE_ADDRESS                       0x00057c00
+#define CE3_BASE_ADDRESS                       0x00058000
+#define CE4_BASE_ADDRESS                       0x00058400
+#define CE5_BASE_ADDRESS                       0x00058800
+#define CE6_BASE_ADDRESS                       0x00058c00
+#define CE7_BASE_ADDRESS                       0x00059000
+#define DBI_BASE_ADDRESS                       0x00060000
+#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS     0x0006c000
+#define PCIE_LOCAL_BASE_ADDRESS                        0x00080000
+
+#define SOC_RESET_CONTROL_OFFSET               0x00000000
+#define SOC_RESET_CONTROL_SI0_RST_MASK         0x00000001
+#define SOC_CPU_CLOCK_OFFSET                   0x00000020
+#define SOC_CPU_CLOCK_STANDARD_LSB             0
+#define SOC_CPU_CLOCK_STANDARD_MASK            0x00000003
+#define SOC_CLOCK_CONTROL_OFFSET               0x00000028
+#define SOC_CLOCK_CONTROL_SI0_CLK_MASK         0x00000001
+#define SOC_SYSTEM_SLEEP_OFFSET                        0x000000c4
+#define SOC_LPO_CAL_OFFSET                     0x000000e0
+#define SOC_LPO_CAL_ENABLE_LSB                 20
+#define SOC_LPO_CAL_ENABLE_MASK                        0x00100000
+
+#define WLAN_RESET_CONTROL_COLD_RST_MASK       0x00000008
+#define WLAN_RESET_CONTROL_WARM_RST_MASK       0x00000004
+#define WLAN_SYSTEM_SLEEP_DISABLE_LSB          0
+#define WLAN_SYSTEM_SLEEP_DISABLE_MASK         0x00000001
+
+#define WLAN_GPIO_PIN0_ADDRESS                 0x00000028
+#define WLAN_GPIO_PIN0_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN1_ADDRESS                 0x0000002c
+#define WLAN_GPIO_PIN1_CONFIG_MASK             0x00007800
+#define WLAN_GPIO_PIN10_ADDRESS                        0x00000050
+#define WLAN_GPIO_PIN11_ADDRESS                        0x00000054
+#define WLAN_GPIO_PIN12_ADDRESS                        0x00000058
+#define WLAN_GPIO_PIN13_ADDRESS                        0x0000005c
+
+#define CLOCK_GPIO_OFFSET                      0xffffffff
+#define CLOCK_GPIO_BT_CLK_OUT_EN_LSB           0
+#define CLOCK_GPIO_BT_CLK_OUT_EN_MASK          0
+
+#define SI_CONFIG_OFFSET                       0x00000000
+#define SI_CONFIG_BIDIR_OD_DATA_LSB            18
+#define SI_CONFIG_BIDIR_OD_DATA_MASK           0x00040000
+#define SI_CONFIG_I2C_LSB                      16
+#define SI_CONFIG_I2C_MASK                     0x00010000
+#define SI_CONFIG_POS_SAMPLE_LSB               7
+#define SI_CONFIG_POS_SAMPLE_MASK              0x00000080
+#define SI_CONFIG_INACTIVE_DATA_LSB            5
+#define SI_CONFIG_INACTIVE_DATA_MASK           0x00000020
+#define SI_CONFIG_INACTIVE_CLK_LSB             4
+#define SI_CONFIG_INACTIVE_CLK_MASK            0x00000010
+#define SI_CONFIG_DIVIDER_LSB                  0
+#define SI_CONFIG_DIVIDER_MASK                 0x0000000f
+#define SI_CS_OFFSET                           0x00000004
+#define SI_CS_DONE_ERR_MASK                    0x00000400
+#define SI_CS_DONE_INT_MASK                    0x00000200
+#define SI_CS_START_LSB                                8
+#define SI_CS_START_MASK                       0x00000100
+#define SI_CS_RX_CNT_LSB                       4
+#define SI_CS_RX_CNT_MASK                      0x000000f0
+#define SI_CS_TX_CNT_LSB                       0
+#define SI_CS_TX_CNT_MASK                      0x0000000f
+
+#define SI_TX_DATA0_OFFSET                     0x00000008
+#define SI_TX_DATA1_OFFSET                     0x0000000c
+#define SI_RX_DATA0_OFFSET                     0x00000010
+#define SI_RX_DATA1_OFFSET                     0x00000014
+
+#define CORE_CTRL_CPU_INTR_MASK                        0x00002000
+#define CORE_CTRL_ADDRESS                      0x0000
+#define PCIE_INTR_ENABLE_ADDRESS               0x0008
+#define PCIE_INTR_CLR_ADDRESS                  0x0014
+#define SCRATCH_3_ADDRESS                      0x0030
+
+/* Firmware indications to the Host via SCRATCH_3 register. */
+#define FW_INDICATOR_ADDRESS   (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
+#define FW_IND_EVENT_PENDING                   1
+#define FW_IND_INITIALIZED                     2
+
+/* HOST_REG interrupt from firmware */
+#define PCIE_INTR_FIRMWARE_MASK                        0x00000400
+#define PCIE_INTR_CE_MASK_ALL                  0x0007f800
+
+#define DRAM_BASE_ADDRESS                      0x00400000
+
+#define MISSING 0
+
+#define SYSTEM_SLEEP_OFFSET                    SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_SYSTEM_SLEEP_OFFSET               SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_RESET_CONTROL_OFFSET              SOC_RESET_CONTROL_OFFSET
+#define CLOCK_CONTROL_OFFSET                   SOC_CLOCK_CONTROL_OFFSET
+#define CLOCK_CONTROL_SI0_CLK_MASK             SOC_CLOCK_CONTROL_SI0_CLK_MASK
+#define RESET_CONTROL_MBOX_RST_MASK            MISSING
+#define RESET_CONTROL_SI0_RST_MASK             SOC_RESET_CONTROL_SI0_RST_MASK
+#define GPIO_BASE_ADDRESS                      WLAN_GPIO_BASE_ADDRESS
+#define GPIO_PIN0_OFFSET                       WLAN_GPIO_PIN0_ADDRESS
+#define GPIO_PIN1_OFFSET                       WLAN_GPIO_PIN1_ADDRESS
+#define GPIO_PIN0_CONFIG_MASK                  WLAN_GPIO_PIN0_CONFIG_MASK
+#define GPIO_PIN1_CONFIG_MASK                  WLAN_GPIO_PIN1_CONFIG_MASK
+#define SI_BASE_ADDRESS                                WLAN_SI_BASE_ADDRESS
+#define SCRATCH_BASE_ADDRESS                   SOC_CORE_BASE_ADDRESS
+#define LOCAL_SCRATCH_OFFSET                   0x18
+#define CPU_CLOCK_OFFSET                       SOC_CPU_CLOCK_OFFSET
+#define LPO_CAL_OFFSET                         SOC_LPO_CAL_OFFSET
+#define GPIO_PIN10_OFFSET                      WLAN_GPIO_PIN10_ADDRESS
+#define GPIO_PIN11_OFFSET                      WLAN_GPIO_PIN11_ADDRESS
+#define GPIO_PIN12_OFFSET                      WLAN_GPIO_PIN12_ADDRESS
+#define GPIO_PIN13_OFFSET                      WLAN_GPIO_PIN13_ADDRESS
+#define CPU_CLOCK_STANDARD_LSB                 SOC_CPU_CLOCK_STANDARD_LSB
+#define CPU_CLOCK_STANDARD_MASK                        SOC_CPU_CLOCK_STANDARD_MASK
+#define LPO_CAL_ENABLE_LSB                     SOC_LPO_CAL_ENABLE_LSB
+#define LPO_CAL_ENABLE_MASK                    SOC_LPO_CAL_ENABLE_MASK
+#define ANALOG_INTF_BASE_ADDRESS               WLAN_ANALOG_INTF_BASE_ADDRESS
+#define MBOX_BASE_ADDRESS                      MISSING
+#define INT_STATUS_ENABLE_ERROR_LSB            MISSING
+#define INT_STATUS_ENABLE_ERROR_MASK           MISSING
+#define INT_STATUS_ENABLE_CPU_LSB              MISSING
+#define INT_STATUS_ENABLE_CPU_MASK             MISSING
+#define INT_STATUS_ENABLE_COUNTER_LSB          MISSING
+#define INT_STATUS_ENABLE_COUNTER_MASK         MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_LSB                MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_MASK       MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB   MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK  MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB    MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK   MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_LSB      MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_MASK     MISSING
+#define INT_STATUS_ENABLE_ADDRESS              MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_LSB          MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_MASK         MISSING
+#define HOST_INT_STATUS_ADDRESS                        MISSING
+#define CPU_INT_STATUS_ADDRESS                 MISSING
+#define ERROR_INT_STATUS_ADDRESS               MISSING
+#define ERROR_INT_STATUS_WAKEUP_MASK           MISSING
+#define ERROR_INT_STATUS_WAKEUP_LSB            MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_MASK     MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_LSB      MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_MASK      MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_LSB       MISSING
+#define COUNT_DEC_ADDRESS                      MISSING
+#define HOST_INT_STATUS_CPU_MASK               MISSING
+#define HOST_INT_STATUS_CPU_LSB                        MISSING
+#define HOST_INT_STATUS_ERROR_MASK             MISSING
+#define HOST_INT_STATUS_ERROR_LSB              MISSING
+#define HOST_INT_STATUS_COUNTER_MASK           MISSING
+#define HOST_INT_STATUS_COUNTER_LSB            MISSING
+#define RX_LOOKAHEAD_VALID_ADDRESS             MISSING
+#define WINDOW_DATA_ADDRESS                    MISSING
+#define WINDOW_READ_ADDR_ADDRESS               MISSING
+#define WINDOW_WRITE_ADDR_ADDRESS              MISSING
+
+#define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB)
+
+#endif /* _HW_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
new file mode 100644 (file)
index 0000000..3446c98
--- /dev/null
@@ -0,0 +1,3066 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 "mac.h"
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+
+#include "core.h"
+#include "debug.h"
+#include "wmi.h"
+#include "htt.h"
+#include "txrx.h"
+
+/**********/
+/* Crypto */
+/**********/
+
+static int ath10k_send_key(struct ath10k_vif *arvif,
+                          struct ieee80211_key_conf *key,
+                          enum set_key_cmd cmd,
+                          const u8 *macaddr)
+{
+       struct wmi_vdev_install_key_arg arg = {
+               .vdev_id = arvif->vdev_id,
+               .key_idx = key->keyidx,
+               .key_len = key->keylen,
+               .key_data = key->key,
+               .macaddr = macaddr,
+       };
+
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+               arg.key_flags = WMI_KEY_PAIRWISE;
+       else
+               arg.key_flags = WMI_KEY_GROUP;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               arg.key_cipher = WMI_CIPHER_AES_CCM;
+               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               arg.key_cipher = WMI_CIPHER_TKIP;
+               arg.key_txmic_len = 8;
+               arg.key_rxmic_len = 8;
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               arg.key_cipher = WMI_CIPHER_WEP;
+               /* AP/IBSS mode requires self-key to be groupwise
+                * Otherwise pairwise key must be set */
+               if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
+                       arg.key_flags = WMI_KEY_PAIRWISE;
+               break;
+       default:
+               ath10k_warn("cipher %d is not supported\n", key->cipher);
+               return -EOPNOTSUPP;
+       }
+
+       if (cmd == DISABLE_KEY) {
+               arg.key_cipher = WMI_CIPHER_NONE;
+               arg.key_data = NULL;
+       }
+
+       return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
+}
+
+static int ath10k_install_key(struct ath10k_vif *arvif,
+                             struct ieee80211_key_conf *key,
+                             enum set_key_cmd cmd,
+                             const u8 *macaddr)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       INIT_COMPLETION(ar->install_key_done);
+
+       ret = ath10k_send_key(arvif, key, cmd, macaddr);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&ar->install_key_done, 3*HZ);
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
+                                       const u8 *addr)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
+               if (arvif->wep_keys[i] == NULL)
+                       continue;
+
+               ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
+                                        addr);
+               if (ret)
+                       return ret;
+
+               peer->keys[i] = arvif->wep_keys[i];
+       }
+
+       return 0;
+}
+
+static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
+                                 const u8 *addr)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       int first_errno = 0;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer)
+               return -ENOENT;
+
+       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+               if (peer->keys[i] == NULL)
+                       continue;
+
+               ret = ath10k_install_key(arvif, peer->keys[i],
+                                        DISABLE_KEY, addr);
+               if (ret && first_errno == 0)
+                       first_errno = ret;
+
+               if (ret)
+                       ath10k_warn("could not remove peer wep key %d (%d)\n",
+                                   i, ret);
+
+               peer->keys[i] = NULL;
+       }
+
+       return first_errno;
+}
+
+static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
+                                struct ieee80211_key_conf *key)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ath10k_peer *peer;
+       u8 addr[ETH_ALEN];
+       int first_errno = 0;
+       int ret;
+       int i;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       for (;;) {
+               /* since ath10k_install_key we can't hold data_lock all the
+                * time, so we try to remove the keys incrementally */
+               spin_lock_bh(&ar->data_lock);
+               i = 0;
+               list_for_each_entry(peer, &ar->peers, list) {
+                       for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+                               if (peer->keys[i] == key) {
+                                       memcpy(addr, peer->addr, ETH_ALEN);
+                                       peer->keys[i] = NULL;
+                                       break;
+                               }
+                       }
+
+                       if (i < ARRAY_SIZE(peer->keys))
+                               break;
+               }
+               spin_unlock_bh(&ar->data_lock);
+
+               if (i == ARRAY_SIZE(peer->keys))
+                       break;
+
+               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
+               if (ret && first_errno == 0)
+                       first_errno = ret;
+
+               if (ret)
+                       ath10k_warn("could not remove key for %pM\n", addr);
+       }
+
+       return first_errno;
+}
+
+
+/*********************/
+/* General utilities */
+/*********************/
+
+static inline enum wmi_phy_mode
+chan_to_phymode(const struct cfg80211_chan_def *chandef)
+{
+       enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+       switch (chandef->chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+                       phymode = MODE_11G;
+                       break;
+               case NL80211_CHAN_WIDTH_20:
+                       phymode = MODE_11NG_HT20;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       phymode = MODE_11NG_HT40;
+                       break;
+               case NL80211_CHAN_WIDTH_80:
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       phymode = MODE_UNKNOWN;
+                       break;
+               }
+               break;
+       case IEEE80211_BAND_5GHZ:
+               switch (chandef->width) {
+               case NL80211_CHAN_WIDTH_20_NOHT:
+                       phymode = MODE_11A;
+                       break;
+               case NL80211_CHAN_WIDTH_20:
+                       phymode = MODE_11NA_HT20;
+                       break;
+               case NL80211_CHAN_WIDTH_40:
+                       phymode = MODE_11NA_HT40;
+                       break;
+               case NL80211_CHAN_WIDTH_80:
+                       phymode = MODE_11AC_VHT80;
+                       break;
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       phymode = MODE_UNKNOWN;
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       WARN_ON(phymode == MODE_UNKNOWN);
+       return phymode;
+}
+
+static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
+{
+/*
+ * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
+ *   0 for no restriction
+ *   1 for 1/4 us
+ *   2 for 1/2 us
+ *   3 for 1 us
+ *   4 for 2 us
+ *   5 for 4 us
+ *   6 for 8 us
+ *   7 for 16 us
+ */
+       switch (mpdudensity) {
+       case 0:
+               return 0;
+       case 1:
+       case 2:
+       case 3:
+       /* Our lower layer calculations limit our precision to
+          1 microsecond */
+               return 1;
+       case 4:
+               return 2;
+       case 5:
+               return 4;
+       case 6:
+               return 8;
+       case 7:
+               return 16;
+       default:
+               return 0;
+       }
+}
+
+static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
+{
+       struct ath10k_peer *peer, *tmp;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+               if (peer->vdev_id != vdev_id)
+                       continue;
+
+               ath10k_warn("removing stale peer %pM from vdev_id %d\n",
+                           peer->addr, vdev_id);
+
+               list_del(&peer->list);
+               kfree(peer);
+       }
+       spin_unlock_bh(&ar->data_lock);
+}
+
+/************************/
+/* Interface management */
+/************************/
+
+static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+{
+       int ret;
+
+       ret = wait_for_completion_timeout(&ar->vdev_setup_done,
+                                         ATH10K_VDEV_SETUP_TIMEOUT_HZ);
+       if (ret == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ath10k_vdev_start(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_conf *conf = &ar->hw->conf;
+       struct ieee80211_channel *channel = conf->chandef.chan;
+       struct wmi_vdev_start_request_arg arg = {};
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       INIT_COMPLETION(ar->vdev_setup_done);
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.dtim_period = arvif->dtim_period;
+       arg.bcn_intval = arvif->beacon_interval;
+
+       arg.channel.freq = channel->center_freq;
+
+       arg.channel.band_center_freq1 = conf->chandef.center_freq1;
+
+       arg.channel.mode = chan_to_phymode(&conf->chandef);
+
+       arg.channel.min_power = channel->max_power * 3;
+       arg.channel.max_power = channel->max_power * 4;
+       arg.channel.max_reg_power = channel->max_reg_power * 4;
+       arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               arg.ssid = arvif->u.ap.ssid;
+               arg.ssid_len = arvif->u.ap.ssid_len;
+               arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+       } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
+               arg.ssid = arvif->vif->bss_conf.ssid;
+               arg.ssid_len = arvif->vif->bss_conf.ssid_len;
+       }
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("WMI vdev start failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       INIT_COMPLETION(ar->vdev_setup_done);
+
+       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+       if (ret) {
+               ath10k_warn("WMI vdev stop failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
+{
+       struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+       struct wmi_vdev_start_request_arg arg = {};
+       enum nl80211_channel_type type;
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
+
+       arg.vdev_id = vdev_id;
+       arg.channel.freq = channel->center_freq;
+       arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+
+       /* TODO setup this dynamically, what in case we
+          don't have any vifs? */
+       arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+
+       arg.channel.min_power = channel->max_power * 3;
+       arg.channel.max_power = channel->max_power * 4;
+       arg.channel.max_reg_power = channel->max_reg_power * 4;
+       arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+       ret = ath10k_wmi_vdev_start(ar, &arg);
+       if (ret) {
+               ath10k_warn("Monitor vdev start failed: ret %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("Monitor vdev setup failed %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
+       if (ret) {
+               ath10k_warn("Monitor vdev up failed: %d\n", ret);
+               goto vdev_stop;
+       }
+
+       ar->monitor_vdev_id = vdev_id;
+       ar->monitor_enabled = true;
+
+       return 0;
+
+vdev_stop:
+       ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+       return ret;
+}
+
+static int ath10k_monitor_stop(struct ath10k *ar)
+{
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* For some reasons, ath10k_wmi_vdev_down() here couse
+        * often ath10k_wmi_vdev_stop() to fail. Next we could
+        * not run monitor vdev and driver reload
+        * required. Don't see such problems we skip
+        * ath10k_wmi_vdev_down() here.
+        */
+
+       ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+       if (ret)
+               ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret)
+               ath10k_warn("Monitor_down sync failed: %d\n", ret);
+
+       ar->monitor_enabled = false;
+       return ret;
+}
+
+static int ath10k_monitor_create(struct ath10k *ar)
+{
+       int bit, ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ar->monitor_present) {
+               ath10k_warn("Monitor mode already enabled\n");
+               return 0;
+       }
+
+       bit = ffs(ar->free_vdev_map);
+       if (bit == 0) {
+               ath10k_warn("No free VDEV slots\n");
+               return -ENOMEM;
+       }
+
+       ar->monitor_vdev_id = bit - 1;
+       ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
+
+       ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
+                                    WMI_VDEV_TYPE_MONITOR,
+                                    0, ar->mac_addr);
+       if (ret) {
+               ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret);
+               goto vdev_fail;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+                  ar->monitor_vdev_id);
+
+       ar->monitor_present = true;
+       return 0;
+
+vdev_fail:
+       /*
+        * Restore the ID to the global map.
+        */
+       ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+       return ret;
+}
+
+static int ath10k_monitor_destroy(struct ath10k *ar)
+{
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (!ar->monitor_present)
+               return 0;
+
+       ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
+       if (ret) {
+               ath10k_warn("WMI vdev monitor delete failed: %d\n", ret);
+               return ret;
+       }
+
+       ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+       ar->monitor_present = false;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+                  ar->monitor_vdev_id);
+       return ret;
+}
+
+static void ath10k_control_beaconing(struct ath10k_vif *arvif,
+                               struct ieee80211_bss_conf *info)
+{
+       int ret = 0;
+
+       if (!info->enable_beacon) {
+               ath10k_vdev_stop(arvif);
+               return;
+       }
+
+       arvif->tx_seq_no = 0x1000;
+
+       ret = ath10k_vdev_start(arvif);
+       if (ret)
+               return;
+
+       ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+       if (ret) {
+               ath10k_warn("Failed to bring up VDEV: %d\n",
+                           arvif->vdev_id);
+               return;
+       }
+       ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+}
+
+static void ath10k_control_ibss(struct ath10k_vif *arvif,
+                               struct ieee80211_bss_conf *info,
+                               const u8 self_peer[ETH_ALEN])
+{
+       int ret = 0;
+
+       if (!info->ibss_joined) {
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
+               if (ret)
+                       ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                                   self_peer, arvif->vdev_id, ret);
+
+               if (is_zero_ether_addr(arvif->u.ibss.bssid))
+                       return;
+
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
+                                        arvif->u.ibss.bssid);
+               if (ret) {
+                       ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
+                                   arvif->u.ibss.bssid, arvif->vdev_id, ret);
+                       return;
+               }
+
+               memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+
+               return;
+       }
+
+       ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
+       if (ret) {
+               ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                           self_peer, arvif->vdev_id, ret);
+               return;
+       }
+
+       ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_ATIM_WINDOW,
+                                       ATH10K_DEFAULT_ATIM);
+       if (ret)
+               ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
+                           arvif->vdev_id, ret);
+}
+
+/*
+ * Review this when mac80211 gains per-interface powersave support.
+ */
+static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       enum wmi_sta_powersave_param param;
+       enum wmi_sta_ps_mode psmode;
+       int ret;
+
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (conf->flags & IEEE80211_CONF_PS) {
+               psmode = WMI_STA_PS_MODE_ENABLED;
+               param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
+
+               ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
+                                                 arvif->vdev_id,
+                                                 param,
+                                                 conf->dynamic_ps_timeout);
+               if (ret) {
+                       ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
+                                   arvif->vdev_id);
+                       return;
+               }
+
+               ar_iter->ret = ret;
+       } else {
+               psmode = WMI_STA_PS_MODE_DISABLED;
+       }
+
+       ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
+                                            psmode);
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
+                           psmode, arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
+                          psmode, arvif->vdev_id);
+}
+
+/**********************/
+/* Station management */
+/**********************/
+
+static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
+                                     struct ath10k_vif *arvif,
+                                     struct ieee80211_sta *sta,
+                                     struct ieee80211_bss_conf *bss_conf,
+                                     struct wmi_peer_assoc_complete_arg *arg)
+{
+       memcpy(arg->addr, sta->addr, ETH_ALEN);
+       arg->vdev_id = arvif->vdev_id;
+       arg->peer_aid = sta->aid;
+       arg->peer_flags |= WMI_PEER_AUTH;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+               /*
+                * Seems FW have problems with Power Save in STA
+                * mode when we setup this parameter to high (eg. 5).
+                * Often we see that FW don't send NULL (with clean P flags)
+                * frame even there is info about buffered frames in beacons.
+                * Sometimes we have to wait more than 10 seconds before FW
+                * will wakeup. Often sending one ping from AP to our device
+                * just fail (more than 50%).
+                *
+                * Seems setting this FW parameter to 1 couse FW
+                * will check every beacon and will wakup immediately
+                * after detection buffered data.
+                */
+               arg->peer_listen_intval = 1;
+       else
+               arg->peer_listen_intval = ar->hw->conf.listen_interval;
+
+       arg->peer_num_spatial_streams = 1;
+
+       /*
+        * The assoc capabilities are available only in managed mode.
+        */
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
+               arg->peer_caps = bss_conf->assoc_capability;
+}
+
+static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
+                                      struct ath10k_vif *arvif,
+                                      struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct ieee80211_vif *vif = arvif->vif;
+       struct ieee80211_bss_conf *info = &vif->bss_conf;
+       struct cfg80211_bss *bss;
+       const u8 *rsnie = NULL;
+       const u8 *wpaie = NULL;
+
+       bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
+                              info->bssid, NULL, 0, 0, 0);
+       if (bss) {
+               const struct cfg80211_bss_ies *ies;
+
+               rcu_read_lock();
+               rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
+
+               ies = rcu_dereference(bss->ies);
+
+               wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                               WLAN_OUI_TYPE_MICROSOFT_WPA,
+                               ies->data,
+                               ies->len);
+               rcu_read_unlock();
+               cfg80211_put_bss(ar->hw->wiphy, bss);
+       }
+
+       /* FIXME: base on RSN IE/WPA IE is a correct idea? */
+       if (rsnie || wpaie) {
+               ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
+               arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+       }
+
+       if (wpaie) {
+               ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
+               arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+       }
+}
+
+static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
+                                     struct ieee80211_sta *sta,
+                                     struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
+       const struct ieee80211_supported_band *sband;
+       const struct ieee80211_rate *rates;
+       u32 ratemask;
+       int i;
+
+       sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
+       ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
+       rates = sband->bitrates;
+
+       rateset->num_rates = 0;
+
+       for (i = 0; i < 32; i++, ratemask >>= 1, rates++) {
+               if (!(ratemask & 1))
+                       continue;
+
+               rateset->rates[rateset->num_rates] = rates->hw_value;
+               rateset->num_rates++;
+       }
+}
+
+static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
+                                  struct ieee80211_sta *sta,
+                                  struct wmi_peer_assoc_complete_arg *arg)
+{
+       const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       int smps;
+       int i, n;
+
+       if (!ht_cap->ht_supported)
+               return;
+
+       arg->peer_flags |= WMI_PEER_HT;
+       arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+                                   ht_cap->ampdu_factor)) - 1;
+
+       arg->peer_mpdu_density =
+               ath10k_parse_mpdudensity(ht_cap->ampdu_density);
+
+       arg->peer_ht_caps = ht_cap->cap;
+       arg->peer_rate_caps |= WMI_RC_HT_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
+               arg->peer_flags |= WMI_PEER_LDPC;
+
+       if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
+               arg->peer_flags |= WMI_PEER_40MHZ;
+               arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
+       }
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
+               arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
+               arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
+               arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
+               arg->peer_flags |= WMI_PEER_STBC;
+       }
+
+       if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
+               u32 stbc;
+               stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
+               stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
+               arg->peer_rate_caps |= stbc;
+               arg->peer_flags |= WMI_PEER_STBC;
+       }
+
+       smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+       smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
+               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+               arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
+       } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
+               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+               arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
+       }
+
+       if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
+               arg->peer_rate_caps |= WMI_RC_TS_FLAG;
+       else if (ht_cap->mcs.rx_mask[1])
+               arg->peer_rate_caps |= WMI_RC_DS_FLAG;
+
+       for (i = 0, n = 0; i < IEEE80211_HT_MCS_MASK_LEN*8; i++)
+               if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
+                       arg->peer_ht_rates.rates[n++] = i;
+
+       arg->peer_ht_rates.num_rates = n;
+       arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+                  arg->peer_ht_rates.num_rates,
+                  arg->peer_num_spatial_streams);
+}
+
+static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
+                                      struct ath10k_vif *arvif,
+                                      struct ieee80211_sta *sta,
+                                      struct ieee80211_bss_conf *bss_conf,
+                                      struct wmi_peer_assoc_complete_arg *arg)
+{
+       u32 uapsd = 0;
+       u32 max_sp = 0;
+
+       if (sta->wme)
+               arg->peer_flags |= WMI_PEER_QOS;
+
+       if (sta->wme && sta->uapsd_queues) {
+               ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+                          sta->uapsd_queues, sta->max_sp);
+
+               arg->peer_flags |= WMI_PEER_APSD;
+               arg->peer_flags |= WMI_RC_UAPSD_FLAG;
+
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+                       uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+                       uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC2_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+                       uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC1_TRIGGER_EN;
+               if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+                       uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
+                                WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
+
+
+               if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
+                       max_sp = sta->max_sp;
+
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_UAPSD,
+                                          uapsd);
+
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_MAX_SP,
+                                          max_sp);
+
+               /* TODO setup this based on STA listen interval and
+                  beacon interval. Currently we don't know
+                  sta->listen_interval - mac80211 patch required.
+                  Currently use 10 seconds */
+               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                          sta->addr,
+                                          WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
+                                          10);
+       }
+}
+
+static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
+                                       struct ath10k_vif *arvif,
+                                       struct ieee80211_sta *sta,
+                                       struct ieee80211_bss_conf *bss_conf,
+                                       struct wmi_peer_assoc_complete_arg *arg)
+{
+       if (bss_conf->qos)
+               arg->peer_flags |= WMI_PEER_QOS;
+}
+
+static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
+                                   struct ieee80211_sta *sta,
+                                   struct wmi_peer_assoc_complete_arg *arg)
+{
+       const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+       if (!vht_cap->vht_supported)
+               return;
+
+       arg->peer_flags |= WMI_PEER_VHT;
+
+       arg->peer_vht_caps = vht_cap->cap;
+
+       if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+               arg->peer_flags |= WMI_PEER_80MHZ;
+
+       arg->peer_vht_rates.rx_max_rate =
+               __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
+       arg->peer_vht_rates.rx_mcs_set =
+               __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+       arg->peer_vht_rates.tx_max_rate =
+               __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+       arg->peer_vht_rates.tx_mcs_set =
+               __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+}
+
+static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
+                                   struct ath10k_vif *arvif,
+                                   struct ieee80211_sta *sta,
+                                   struct ieee80211_bss_conf *bss_conf,
+                                   struct wmi_peer_assoc_complete_arg *arg)
+{
+       switch (arvif->vdev_type) {
+       case WMI_VDEV_TYPE_AP:
+               ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+               break;
+       case WMI_VDEV_TYPE_STA:
+               ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
+                                       struct ath10k_vif *arvif,
+                                       struct ieee80211_sta *sta,
+                                       struct wmi_peer_assoc_complete_arg *arg)
+{
+       enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+       /* FIXME: add VHT */
+
+       switch (ar->hw->conf.chandef.chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               if (sta->ht_cap.ht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11NG_HT40;
+                       else
+                               phymode = MODE_11NG_HT20;
+               } else {
+                       phymode = MODE_11G;
+               }
+
+               break;
+       case IEEE80211_BAND_5GHZ:
+               if (sta->ht_cap.ht_supported) {
+                       if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+                               phymode = MODE_11NA_HT40;
+                       else
+                               phymode = MODE_11NA_HT20;
+               } else {
+                       phymode = MODE_11A;
+               }
+
+               break;
+       default:
+               break;
+       }
+
+       arg->peer_phymode = phymode;
+       WARN_ON(phymode == MODE_UNKNOWN);
+}
+
+static int ath10k_peer_assoc(struct ath10k *ar,
+                            struct ath10k_vif *arvif,
+                            struct ieee80211_sta *sta,
+                            struct ieee80211_bss_conf *bss_conf)
+{
+       struct wmi_peer_assoc_complete_arg arg;
+
+       memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+
+       ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
+       ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
+       ath10k_peer_assoc_h_rates(ar, sta, &arg);
+       ath10k_peer_assoc_h_ht(ar, sta, &arg);
+       ath10k_peer_assoc_h_vht(ar, sta, &arg);
+       ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
+       ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+
+       return ath10k_wmi_peer_assoc(ar, &arg);
+}
+
+/* can be called only in mac80211 callbacks due to `key_count` usage */
+static void ath10k_bss_assoc(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_bss_conf *bss_conf)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ieee80211_sta *ap_sta;
+       int ret;
+
+       rcu_read_lock();
+
+       ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
+       if (!ap_sta) {
+               ath10k_warn("Failed to find station entry for %pM\n",
+                           bss_conf->bssid);
+               rcu_read_unlock();
+               return;
+       }
+
+       ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+       if (ret) {
+               ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+               rcu_read_unlock();
+               return;
+       }
+
+       rcu_read_unlock();
+
+       ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
+                                bss_conf->bssid);
+       if (ret)
+               ath10k_warn("VDEV: %d up failed: ret %d\n",
+                           arvif->vdev_id, ret);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "VDEV: %d associated, BSSID: %pM, AID: %d\n",
+                          arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+}
+
+/*
+ * FIXME: flush TIDs
+ */
+static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       /*
+        * For some reason, calling VDEV-DOWN before VDEV-STOP
+        * makes the FW to send frames via HTT after disassociation.
+        * No idea why this happens, even though VDEV-DOWN is supposed
+        * to be analogous to link down, so just stop the VDEV.
+        */
+       ret = ath10k_vdev_stop(arvif);
+       if (!ret)
+               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
+                          arvif->vdev_id);
+
+       /*
+        * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
+        * report beacons from previously associated network through HTT.
+        * This in turn would spam mac80211 WARN_ON if we bring down all
+        * interfaces as it expects there is no rx when no interface is
+        * running.
+        */
+       ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+       if (ret)
+               ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
+                          arvif->vdev_id, ret);
+
+       ath10k_wmi_flush_tx(ar);
+
+       arvif->def_wep_key_index = 0;
+}
+
+static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
+                               struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+       if (ret) {
+               ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+               return ret;
+       }
+
+       ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+       if (ret) {
+               ath10k_warn("could not install peer wep keys (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
+                                  struct ieee80211_sta *sta)
+{
+       int ret = 0;
+
+       ret = ath10k_clear_peer_keys(arvif, sta->addr);
+       if (ret) {
+               ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+/**************/
+/* Regulatory */
+/**************/
+
+static int ath10k_update_channel_list(struct ath10k *ar)
+{
+       struct ieee80211_hw *hw = ar->hw;
+       struct ieee80211_supported_band **bands;
+       enum ieee80211_band band;
+       struct ieee80211_channel *channel;
+       struct wmi_scan_chan_list_arg arg = {0};
+       struct wmi_channel_arg *ch;
+       bool passive;
+       int len;
+       int ret;
+       int i;
+
+       bands = hw->wiphy->bands;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!bands[band])
+                       continue;
+
+               for (i = 0; i < bands[band]->n_channels; i++) {
+                       if (bands[band]->channels[i].flags &
+                           IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       arg.n_channels++;
+               }
+       }
+
+       len = sizeof(struct wmi_channel_arg) * arg.n_channels;
+       arg.channels = kzalloc(len, GFP_KERNEL);
+       if (!arg.channels)
+               return -ENOMEM;
+
+       ch = arg.channels;
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!bands[band])
+                       continue;
+
+               for (i = 0; i < bands[band]->n_channels; i++) {
+                       channel = &bands[band]->channels[i];
+
+                       if (channel->flags & IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       ch->allow_ht   = true;
+
+                       /* FIXME: when should we really allow VHT? */
+                       ch->allow_vht = true;
+
+                       ch->allow_ibss =
+                               !(channel->flags & IEEE80211_CHAN_NO_IBSS);
+
+                       ch->ht40plus =
+                               !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
+
+                       passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
+                       ch->passive = passive;
+
+                       ch->freq = channel->center_freq;
+                       ch->min_power = channel->max_power * 3;
+                       ch->max_power = channel->max_power * 4;
+                       ch->max_reg_power = channel->max_reg_power * 4;
+                       ch->max_antenna_gain = channel->max_antenna_gain;
+                       ch->reg_class_id = 0; /* FIXME */
+
+                       /* FIXME: why use only legacy modes, why not any
+                        * HT/VHT modes? Would that even make any
+                        * difference? */
+                       if (channel->band == IEEE80211_BAND_2GHZ)
+                               ch->mode = MODE_11G;
+                       else
+                               ch->mode = MODE_11A;
+
+                       if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN))
+                               continue;
+
+                       ath10k_dbg(ATH10K_DBG_WMI,
+                                  "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+                                  __func__, ch - arg.channels, arg.n_channels,
+                                  ch->freq, ch->max_power, ch->max_reg_power,
+                                  ch->max_antenna_gain, ch->mode);
+
+                       ch++;
+               }
+       }
+
+       ret = ath10k_wmi_scan_chan_list(ar, &arg);
+       kfree(arg.channels);
+
+       return ret;
+}
+
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+                               struct regulatory_request *request)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct reg_dmn_pair_mapping *regpair;
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+       ret = ath10k_update_channel_list(ar);
+       if (ret)
+               ath10k_warn("could not update channel list (%d)\n", ret);
+
+       regpair = ar->ath_common.regulatory.regpair;
+       /* Target allows setting up per-band regdomain but ath_common provides
+        * a combined one only */
+       ret = ath10k_wmi_pdev_set_regdomain(ar,
+                                           regpair->regDmnEnum,
+                                           regpair->regDmnEnum, /* 2ghz */
+                                           regpair->regDmnEnum, /* 5ghz */
+                                           regpair->reg_2ghz_ctl,
+                                           regpair->reg_5ghz_ctl);
+       if (ret)
+               ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
+
+/***************/
+/* TX handlers */
+/***************/
+
+/*
+ * Frames sent to the FW have to be in "Native Wifi" format.
+ * Strip the QoS field from the 802.11 header.
+ */
+static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
+                                      struct ieee80211_tx_control *control,
+                                      struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       u8 *qos_ctl;
+
+       if (!ieee80211_is_data_qos(hdr->frame_control))
+               return;
+
+       qos_ctl = ieee80211_get_qos_ctl(hdr);
+       memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
+               skb->len - ieee80211_hdrlen(hdr->frame_control));
+       skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+}
+
+static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       int ret;
+
+       /* TODO AP mode should be implemented */
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       if (!ieee80211_has_protected(hdr->frame_control))
+               return;
+
+       if (!key)
+               return;
+
+       if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+           key->cipher != WLAN_CIPHER_SUITE_WEP104)
+               return;
+
+       if (key->keyidx == arvif->def_wep_key_index)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_DEF_KEYID,
+                                       key->keyidx);
+       if (ret) {
+               ath10k_warn("could not update wep keyidx (%d)\n", ret);
+               return;
+       }
+
+       arvif->def_wep_key_index = key->keyidx;
+}
+
+static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       /* This is case only for P2P_GO */
+       if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
+           arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return;
+
+       if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
+               spin_lock_bh(&ar->data_lock);
+               if (arvif->u.ap.noa_data)
+                       if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len,
+                                             GFP_ATOMIC))
+                               memcpy(skb_put(skb, arvif->u.ap.noa_len),
+                                      arvif->u.ap.noa_data,
+                                      arvif->u.ap.noa_len);
+               spin_unlock_bh(&ar->data_lock);
+       }
+}
+
+static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int ret;
+
+       if (ieee80211_is_mgmt(hdr->frame_control))
+               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+       else if (ieee80211_is_nullfunc(hdr->frame_control))
+               /* FW does not report tx status properly for NullFunc frames
+                * unless they are sent through mgmt tx path. mac80211 sends
+                * those frames when it detects link/beacon loss and depends on
+                * the tx status to be correct. */
+               ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+       else
+               ret = ath10k_htt_tx(ar->htt, skb);
+
+       if (ret) {
+               ath10k_warn("tx failed (%d). dropping packet.\n", ret);
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_offchan_tx_purge(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->offchan_tx_queue);
+               if (!skb)
+                       break;
+
+               ieee80211_free_txskb(ar->hw, skb);
+       }
+}
+
+void ath10k_offchan_tx_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
+       struct ath10k_peer *peer;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb;
+       const u8 *peer_addr;
+       int vdev_id;
+       int ret;
+
+       /* FW requirement: We must create a peer before FW will send out
+        * an offchannel frame. Otherwise the frame will be stuck and
+        * never transmitted. We delete the peer upon tx completion.
+        * It is unlikely that a peer for offchannel tx will already be
+        * present. However it may be in some rare cases so account for that.
+        * Otherwise we might remove a legitimate peer and break stuff. */
+
+       for (;;) {
+               skb = skb_dequeue(&ar->offchan_tx_queue);
+               if (!skb)
+                       break;
+
+               mutex_lock(&ar->conf_mutex);
+
+               ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+                          skb);
+
+               hdr = (struct ieee80211_hdr *)skb->data;
+               peer_addr = ieee80211_get_DA(hdr);
+               vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+
+               spin_lock_bh(&ar->data_lock);
+               peer = ath10k_peer_find(ar, vdev_id, peer_addr);
+               spin_unlock_bh(&ar->data_lock);
+
+               if (peer)
+                       ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
+                                  peer_addr, vdev_id);
+
+               if (!peer) {
+                       ret = ath10k_peer_create(ar, vdev_id, peer_addr);
+                       if (ret)
+                               ath10k_warn("peer %pM on vdev %d not created (%d)\n",
+                                           peer_addr, vdev_id, ret);
+               }
+
+               spin_lock_bh(&ar->data_lock);
+               INIT_COMPLETION(ar->offchan_tx_completed);
+               ar->offchan_tx_skb = skb;
+               spin_unlock_bh(&ar->data_lock);
+
+               ath10k_tx_htt(ar, skb);
+
+               ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
+                                                 3 * HZ);
+               if (ret <= 0)
+                       ath10k_warn("timed out waiting for offchannel skb %p\n",
+                                   skb);
+
+               if (!peer) {
+                       ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
+                       if (ret)
+                               ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
+                                           peer_addr, vdev_id, ret);
+               }
+
+               mutex_unlock(&ar->conf_mutex);
+       }
+}
+
+/************/
+/* Scanning */
+/************/
+
+/*
+ * This gets called if we dont get a heart-beat during scan.
+ * This may indicate the FW has hung and we need to abort the
+ * scan manually to prevent cancel_hw_scan() from deadlocking
+ */
+void ath10k_reset_scan(unsigned long ptr)
+{
+       struct ath10k *ar = (struct ath10k *)ptr;
+
+       spin_lock_bh(&ar->data_lock);
+       if (!ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               return;
+       }
+
+       ath10k_warn("scan timeout. resetting. fw issue?\n");
+
+       if (ar->scan.is_roc)
+               ieee80211_remain_on_channel_expired(ar->hw);
+       else
+               ieee80211_scan_completed(ar->hw, 1 /* aborted */);
+
+       ar->scan.in_progress = false;
+       complete_all(&ar->scan.completed);
+       spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath10k_abort_scan(struct ath10k *ar)
+{
+       struct wmi_stop_scan_arg arg = {
+               .req_id = 1, /* FIXME */
+               .req_type = WMI_SCAN_STOP_ONE,
+               .u.scan_id = ATH10K_SCAN_ID,
+       };
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       del_timer_sync(&ar->scan.timeout);
+
+       spin_lock_bh(&ar->data_lock);
+       if (!ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               return 0;
+       }
+
+       ar->scan.aborting = true;
+       spin_unlock_bh(&ar->data_lock);
+
+       ret = ath10k_wmi_stop_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               return -EIO;
+       }
+
+       ath10k_wmi_flush_tx(ar);
+
+       ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
+       if (ret == 0)
+               ath10k_warn("timed out while waiting for scan to stop\n");
+
+       /* scan completion may be done right after we timeout here, so let's
+        * check the in_progress and tell mac80211 scan is completed. if we
+        * don't do that and FW fails to send us scan completion indication
+        * then userspace won't be able to scan anymore */
+       ret = 0;
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               ath10k_warn("could not stop scan. its still in progress\n");
+               ar->scan.in_progress = false;
+               ath10k_offchan_tx_purge(ar);
+               ret = -ETIMEDOUT;
+       }
+       spin_unlock_bh(&ar->data_lock);
+
+       return ret;
+}
+
+static int ath10k_start_scan(struct ath10k *ar,
+                            const struct wmi_start_scan_arg *arg)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ret = ath10k_wmi_start_scan(ar, arg);
+       if (ret)
+               return ret;
+
+       /* make sure we submit the command so the completion
+       * timeout makes sense */
+       ath10k_wmi_flush_tx(ar);
+
+       ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
+       if (ret == 0) {
+               ath10k_abort_scan(ar);
+               return ret;
+       }
+
+       /* the scan can complete earlier, before we even
+        * start the timer. in that case the timer handler
+        * checks ar->scan.in_progress and bails out if its
+        * false. Add a 200ms margin to account event/command
+        * processing. */
+       mod_timer(&ar->scan.timeout, jiffies +
+                 msecs_to_jiffies(arg->max_scan_time+200));
+       return 0;
+}
+
+/**********************/
+/* mac80211 callbacks */
+/**********************/
+
+static void ath10k_tx(struct ieee80211_hw *hw,
+                     struct ieee80211_tx_control *control,
+                     struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = NULL;
+       u32 vdev_id = 0;
+       u8 tid;
+
+       if (info->control.vif) {
+               arvif = ath10k_vif_to_arvif(info->control.vif);
+               vdev_id = arvif->vdev_id;
+       } else if (ar->monitor_enabled) {
+               vdev_id = ar->monitor_vdev_id;
+       }
+
+       /* We should disable CCK RATE due to P2P */
+       if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+               ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+       /* we must calculate tid before we apply qos workaround
+        * as we'd lose the qos control field */
+       tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+       if (ieee80211_is_data_qos(hdr->frame_control) &&
+           is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+       }
+
+       ath10k_tx_h_qos_workaround(hw, control, skb);
+       ath10k_tx_h_update_wep_key(skb);
+       ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+       ath10k_tx_h_seq_no(skb);
+
+       memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
+       ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
+       ATH10K_SKB_CB(skb)->htt.tid = tid;
+
+       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               spin_lock_bh(&ar->data_lock);
+               ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+               ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+               spin_unlock_bh(&ar->data_lock);
+
+               ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
+
+               skb_queue_tail(&ar->offchan_tx_queue, skb);
+               ieee80211_queue_work(hw, &ar->offchan_tx_work);
+               return;
+       }
+
+       ath10k_tx_htt(ar, skb);
+}
+
+/*
+ * Initialize various parameters with default vaules.
+ */
+static int ath10k_start(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+       if (ret)
+               ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
+                           ret);
+
+       ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+       if (ret)
+               ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
+                           ret);
+
+       return 0;
+}
+
+static void ath10k_stop(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       /* avoid leaks in case FW never confirms scan for offchannel */
+       cancel_work_sync(&ar->offchan_tx_work);
+       ath10k_offchan_tx_purge(ar);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
+       int ret = 0;
+       u32 flags;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
+                          conf->chandef.chan->center_freq);
+               spin_lock_bh(&ar->data_lock);
+               ar->rx_channel = conf->chandef.chan;
+               spin_unlock_bh(&ar->data_lock);
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+               ar_iter.ar = ar;
+               flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+
+               ieee80211_iterate_active_interfaces_atomic(hw,
+                                                          flags,
+                                                          ath10k_ps_iter,
+                                                          &ar_iter);
+
+               ret = ar_iter.ret;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               if (conf->flags & IEEE80211_CONF_MONITOR)
+                       ret = ath10k_monitor_create(ar);
+               else
+                       ret = ath10k_monitor_destroy(ar);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+/*
+ * TODO:
+ * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
+ * because we will send mgmt frames without CCK. This requirement
+ * for P2P_FIND/GO_NEG should be handled by checking CCK flag
+ * in the TX packet.
+ */
+static int ath10k_add_interface(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       enum wmi_sta_powersave_param param;
+       int ret = 0;
+       u32 value;
+       int bit;
+
+       mutex_lock(&ar->conf_mutex);
+
+       arvif->ar = ar;
+       arvif->vif = vif;
+
+       if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
+               ath10k_warn("Only one monitor interface allowed\n");
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       bit = ffs(ar->free_vdev_map);
+       if (bit == 0) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       arvif->vdev_id = bit - 1;
+       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
+       ar->free_vdev_map &= ~(1 << arvif->vdev_id);
+
+       if (ar->p2p)
+               arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_STATION:
+               arvif->vdev_type = WMI_VDEV_TYPE_STA;
+               if (vif->p2p)
+                       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
+               break;
+       case NL80211_IFTYPE_AP:
+               arvif->vdev_type = WMI_VDEV_TYPE_AP;
+
+               if (vif->p2p)
+                       arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO;
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+                  arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
+
+       ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
+                                    arvif->vdev_subtype, vif->addr);
+       if (ret) {
+               ath10k_warn("WMI vdev create failed: ret %d\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
+                                       arvif->def_wep_key_index);
+       if (ret)
+               ath10k_warn("Failed to set default keyid: %d\n", ret);
+
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+                                       ATH10K_HW_TXRX_NATIVE_WIFI);
+       if (ret)
+               ath10k_warn("Failed to set TX encap: %d\n", ret);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
+               if (ret) {
+                       ath10k_warn("Failed to create peer for AP: %d\n", ret);
+                       goto exit;
+               }
+       }
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+               param = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
+               value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+
+               param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
+               value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+
+               param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
+               value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
+               ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                                 param, value);
+               if (ret)
+                       ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+       }
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+               ar->monitor_present = true;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static void ath10k_remove_interface(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+
+       ar->free_vdev_map |= 1 << (arvif->vdev_id);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
+               if (ret)
+                       ath10k_warn("Failed to remove peer for AP: %d\n", ret);
+
+               kfree(arvif->u.ap.noa_data);
+       }
+
+       ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+       if (ret)
+               ath10k_warn("WMI vdev delete failed: %d\n", ret);
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+               ar->monitor_present = false;
+
+       ath10k_peer_cleanup(ar, arvif->vdev_id);
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+/*
+ * FIXME: Has to be verified.
+ */
+#define SUPPORTED_FILTERS                      \
+       (FIF_PROMISC_IN_BSS |                   \
+       FIF_ALLMULTI |                          \
+       FIF_CONTROL |                           \
+       FIF_PSPOLL |                            \
+       FIF_OTHER_BSS |                         \
+       FIF_BCN_PRBRESP_PROMISC |               \
+       FIF_PROBE_REQ |                         \
+       FIF_FCSFAIL)
+
+static void ath10k_configure_filter(struct ieee80211_hw *hw,
+                                   unsigned int changed_flags,
+                                   unsigned int *total_flags,
+                                   u64 multicast)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       changed_flags &= SUPPORTED_FILTERS;
+       *total_flags &= SUPPORTED_FILTERS;
+       ar->filter_flags = *total_flags;
+
+       if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+           !ar->monitor_enabled) {
+               ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+               if (ret)
+                       ath10k_warn("Unable to start monitor mode\n");
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
+       } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+                  ar->monitor_enabled) {
+               ret = ath10k_monitor_stop(ar);
+               if (ret)
+                       ath10k_warn("Unable to stop monitor mode\n");
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_bss_conf *info,
+                                   u32 changed)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & BSS_CHANGED_IBSS)
+               ath10k_control_ibss(arvif, info, vif->addr);
+
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               arvif->beacon_interval = info->beacon_int;
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_BEACON_INTERVAL,
+                                               arvif->beacon_interval);
+               if (ret)
+                       ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Beacon interval: %d set for VDEV: %d\n",
+                                  arvif->beacon_interval, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_BEACON) {
+               ret = ath10k_wmi_pdev_set_param(ar,
+                                               WMI_PDEV_PARAM_BEACON_TX_MODE,
+                                               WMI_BEACON_STAGGERED_MODE);
+               if (ret)
+                       ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set staggered beacon mode for VDEV: %d\n",
+                                  arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_BEACON_INFO) {
+               arvif->dtim_period = info->dtim_period;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_DTIM_PERIOD,
+                                               arvif->dtim_period);
+               if (ret)
+                       ath10k_warn("Failed to set dtim period for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set dtim period: %d for VDEV: %d\n",
+                                  arvif->dtim_period, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_SSID &&
+           vif->type == NL80211_IFTYPE_AP) {
+               arvif->u.ap.ssid_len = info->ssid_len;
+               if (info->ssid_len)
+                       memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len);
+               arvif->u.ap.hidden_ssid = info->hidden_ssid;
+       }
+
+       if (changed & BSS_CHANGED_BSSID) {
+               if (!is_zero_ether_addr(info->bssid)) {
+                       ret = ath10k_peer_create(ar, arvif->vdev_id,
+                                                info->bssid);
+                       if (ret)
+                               ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+                                           info->bssid, arvif->vdev_id);
+                       else
+                               ath10k_dbg(ATH10K_DBG_MAC,
+                                          "Added peer: %pM for VDEV: %d\n",
+                                          info->bssid, arvif->vdev_id);
+
+
+                       if (vif->type == NL80211_IFTYPE_STATION) {
+                               /*
+                                * this is never erased as we it for crypto key
+                                * clearing; this is FW requirement
+                                */
+                               memcpy(arvif->u.sta.bssid, info->bssid,
+                                      ETH_ALEN);
+
+                               ret = ath10k_vdev_start(arvif);
+                               if (!ret)
+                                       ath10k_dbg(ATH10K_DBG_MAC,
+                                                  "VDEV: %d started with BSSID: %pM\n",
+                                                  arvif->vdev_id, info->bssid);
+                       }
+
+                       /*
+                        * Mac80211 does not keep IBSS bssid when leaving IBSS,
+                        * so driver need to store it. It is needed when leaving
+                        * IBSS in order to remove BSSID peer.
+                        */
+                       if (vif->type == NL80211_IFTYPE_ADHOC)
+                               memcpy(arvif->u.ibss.bssid, info->bssid,
+                                      ETH_ALEN);
+               }
+       }
+
+       if (changed & BSS_CHANGED_BEACON_ENABLED)
+               ath10k_control_beaconing(arvif, info);
+
+       if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+               u32 cts_prot;
+               if (info->use_cts_prot)
+                       cts_prot = 1;
+               else
+                       cts_prot = 0;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_ENABLE_RTSCTS,
+                                               cts_prot);
+               if (ret)
+                       ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set CTS prot: %d for VDEV: %d\n",
+                                  cts_prot, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               u32 slottime;
+               if (info->use_short_slot)
+                       slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
+
+               else
+                       slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_SLOT_TIME,
+                                               slottime);
+               if (ret)
+                       ath10k_warn("Failed to set erp slot for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set slottime: %d for VDEV: %d\n",
+                                  slottime, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               u32 preamble;
+               if (info->use_short_preamble)
+                       preamble = WMI_VDEV_PREAMBLE_SHORT;
+               else
+                       preamble = WMI_VDEV_PREAMBLE_LONG;
+
+               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                               WMI_VDEV_PARAM_PREAMBLE,
+                                               preamble);
+               if (ret)
+                       ath10k_warn("Failed to set preamble for VDEV: %d\n",
+                                   arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Set preamble: %d for VDEV: %d\n",
+                                  preamble, arvif->vdev_id);
+       }
+
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (info->assoc)
+                       ath10k_bss_assoc(hw, vif, info);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_hw_scan(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif,
+                         struct cfg80211_scan_request *req)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_start_scan_arg arg;
+       int ret = 0;
+       int i;
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       INIT_COMPLETION(ar->scan.started);
+       INIT_COMPLETION(ar->scan.completed);
+       ar->scan.in_progress = true;
+       ar->scan.aborting = false;
+       ar->scan.is_roc = false;
+       ar->scan.vdev_id = arvif->vdev_id;
+       spin_unlock_bh(&ar->data_lock);
+
+       memset(&arg, 0, sizeof(arg));
+       ath10k_wmi_start_scan_init(ar, &arg);
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH10K_SCAN_ID;
+
+       if (!req->no_cck)
+               arg.scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES;
+
+       if (req->ie_len) {
+               arg.ie_len = req->ie_len;
+               memcpy(arg.ie, req->ie, arg.ie_len);
+       }
+
+       if (req->n_ssids) {
+               arg.n_ssids = req->n_ssids;
+               for (i = 0; i < arg.n_ssids; i++) {
+                       arg.ssids[i].len  = req->ssids[i].ssid_len;
+                       arg.ssids[i].ssid = req->ssids[i].ssid;
+               }
+       }
+
+       if (req->n_channels) {
+               arg.n_channels = req->n_channels;
+               for (i = 0; i < arg.n_channels; i++)
+                       arg.channels[i] = req->channels[i]->center_freq;
+       }
+
+       ret = ath10k_start_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not start hw scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               spin_unlock_bh(&ar->data_lock);
+       }
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+       ret = ath10k_abort_scan(ar);
+       if (ret) {
+               ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n",
+                           ret);
+               ieee80211_scan_completed(hw, 1 /* aborted */);
+       }
+       mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_peer *peer;
+       const u8 *peer_addr;
+       bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                     key->cipher == WLAN_CIPHER_SUITE_WEP104;
+       int ret = 0;
+
+       if (key->keyidx > WMI_MAX_KEY_INDEX)
+               return -ENOSPC;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (sta)
+               peer_addr = sta->addr;
+       else if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+               peer_addr = vif->bss_conf.bssid;
+       else
+               peer_addr = vif->addr;
+
+       key->hw_key_idx = key->keyidx;
+
+       /* the peer should not disappear in mid-way (unless FW goes awry) since
+        * we already hold conf_mutex. we just make sure its there now. */
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!peer) {
+               if (cmd == SET_KEY) {
+                       ath10k_warn("cannot install key for non-existent peer %pM\n",
+                                   peer_addr);
+                       ret = -EOPNOTSUPP;
+                       goto exit;
+               } else {
+                       /* if the peer doesn't exist there is no key to disable
+                        * anymore */
+                       goto exit;
+               }
+       }
+
+       if (is_wep) {
+               if (cmd == SET_KEY)
+                       arvif->wep_keys[key->keyidx] = key;
+               else
+                       arvif->wep_keys[key->keyidx] = NULL;
+
+               if (cmd == DISABLE_KEY)
+                       ath10k_clear_vdev_key(arvif, key);
+       }
+
+       ret = ath10k_install_key(arvif, key, cmd, peer_addr);
+       if (ret) {
+               ath10k_warn("ath10k_install_key failed (%d)\n", ret);
+               goto exit;
+       }
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+       if (peer && cmd == SET_KEY)
+               peer->keys[key->keyidx] = key;
+       else if (peer && cmd == DISABLE_KEY)
+               peer->keys[key->keyidx] = NULL;
+       else if (peer == NULL)
+               /* impossible unless FW goes crazy */
+               ath10k_warn("peer %pM disappeared!\n", peer_addr);
+       spin_unlock_bh(&ar->data_lock);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_sta_state(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta,
+                           enum ieee80211_sta_state old_state,
+                           enum ieee80211_sta_state new_state)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE &&
+           vif->type != NL80211_IFTYPE_STATION) {
+               /*
+                * New station addition.
+                */
+               ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
+               if (ret)
+                       ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Added peer: %pM for VDEV: %d\n",
+                                  sta->addr, arvif->vdev_id);
+       } else if ((old_state == IEEE80211_STA_NONE &&
+                   new_state == IEEE80211_STA_NOTEXIST)) {
+               /*
+                * Existing station deletion.
+                */
+               ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+               if (ret)
+                       ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Removed peer: %pM for VDEV: %d\n",
+                                  sta->addr, arvif->vdev_id);
+
+               if (vif->type == NL80211_IFTYPE_STATION)
+                       ath10k_bss_disassoc(hw, vif);
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               /*
+                * New association.
+                */
+               ret = ath10k_station_assoc(ar, arvif, sta);
+               if (ret)
+                       ath10k_warn("Failed to associate station: %pM\n",
+                                   sta->addr);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Station %pM moved to assoc state\n",
+                                  sta->addr);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               /*
+                * Disassociation.
+                */
+               ret = ath10k_station_disassoc(ar, arvif, sta);
+               if (ret)
+                       ath10k_warn("Failed to disassociate station: %pM\n",
+                                   sta->addr);
+               else
+                       ath10k_dbg(ATH10K_DBG_MAC,
+                                  "Station %pM moved to disassociated state\n",
+                                  sta->addr);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
+                                u16 ac, bool enable)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 value = 0;
+       int ret = 0;
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+               return 0;
+
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_VI:
+               value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_BE:
+               value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
+               break;
+       case IEEE80211_AC_BK:
+               value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
+                       WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
+               break;
+       }
+
+       if (enable)
+               arvif->u.sta.uapsd |= value;
+       else
+               arvif->u.sta.uapsd &= ~value;
+
+       ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                         WMI_STA_PS_PARAM_UAPSD,
+                                         arvif->u.sta.uapsd);
+       if (ret) {
+               ath10k_warn("could not set uapsd params %d\n", ret);
+               goto exit;
+       }
+
+       if (arvif->u.sta.uapsd)
+               value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD;
+       else
+               value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+
+       ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+                                         WMI_STA_PS_PARAM_RX_WAKE_POLICY,
+                                         value);
+       if (ret)
+               ath10k_warn("could not set rx wake param %d\n", ret);
+
+exit:
+       return ret;
+}
+
+static int ath10k_conf_tx(struct ieee80211_hw *hw,
+                         struct ieee80211_vif *vif, u16 ac,
+                         const struct ieee80211_tx_queue_params *params)
+{
+       struct ath10k *ar = hw->priv;
+       struct wmi_wmm_params_arg *p = NULL;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               p = &ar->wmm_params.ac_vo;
+               break;
+       case IEEE80211_AC_VI:
+               p = &ar->wmm_params.ac_vi;
+               break;
+       case IEEE80211_AC_BE:
+               p = &ar->wmm_params.ac_be;
+               break;
+       case IEEE80211_AC_BK:
+               p = &ar->wmm_params.ac_bk;
+               break;
+       }
+
+       if (WARN_ON(!p)) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       p->cwmin = params->cw_min;
+       p->cwmax = params->cw_max;
+       p->aifs = params->aifs;
+
+       /*
+        * The channel time duration programmed in the HW is in absolute
+        * microseconds, while mac80211 gives the txop in units of
+        * 32 microseconds.
+        */
+       p->txop = params->txop * 32;
+
+       /* FIXME: FW accepts wmm params per hw, not per vif */
+       ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
+       if (ret) {
+               ath10k_warn("could not set wmm params %d\n", ret);
+               goto exit;
+       }
+
+       ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
+       if (ret)
+               ath10k_warn("could not set sta uapsd %d\n", ret);
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+#define ATH10K_ROC_TIMEOUT_HZ (2*HZ)
+
+static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_channel *chan,
+                                   int duration,
+                                   enum ieee80211_roc_type type)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct wmi_start_scan_arg arg;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       spin_lock_bh(&ar->data_lock);
+       if (ar->scan.in_progress) {
+               spin_unlock_bh(&ar->data_lock);
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       INIT_COMPLETION(ar->scan.started);
+       INIT_COMPLETION(ar->scan.completed);
+       INIT_COMPLETION(ar->scan.on_channel);
+       ar->scan.in_progress = true;
+       ar->scan.aborting = false;
+       ar->scan.is_roc = true;
+       ar->scan.vdev_id = arvif->vdev_id;
+       ar->scan.roc_freq = chan->center_freq;
+       spin_unlock_bh(&ar->data_lock);
+
+       memset(&arg, 0, sizeof(arg));
+       ath10k_wmi_start_scan_init(ar, &arg);
+       arg.vdev_id = arvif->vdev_id;
+       arg.scan_id = ATH10K_SCAN_ID;
+       arg.n_channels = 1;
+       arg.channels[0] = chan->center_freq;
+       arg.dwell_time_active = duration;
+       arg.dwell_time_passive = duration;
+       arg.max_scan_time = 2 * duration;
+       arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
+       arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ;
+
+       ret = ath10k_start_scan(ar, &arg);
+       if (ret) {
+               ath10k_warn("could not start roc scan (%d)\n", ret);
+               spin_lock_bh(&ar->data_lock);
+               ar->scan.in_progress = false;
+               spin_unlock_bh(&ar->data_lock);
+               goto exit;
+       }
+
+       ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
+       if (ret == 0) {
+               ath10k_warn("could not switch to channel for roc scan\n");
+               ath10k_abort_scan(ar);
+               ret = -ETIMEDOUT;
+               goto exit;
+       }
+
+       ret = 0;
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+       ath10k_abort_scan(ar);
+       mutex_unlock(&ar->conf_mutex);
+
+       return 0;
+}
+
+/*
+ * Both RTS and Fragmentation threshold are interface-specific
+ * in ath10k, but device-specific in mac80211.
+ */
+static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
+
+       rts = min_t(u32, rts, ATH10K_RTS_MAX);
+
+       ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+                                                WMI_VDEV_PARAM_RTS_THRESHOLD,
+                                                rts);
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
+                           arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "Set RTS threshold: %d for VDEV: %d\n",
+                          rts, arvif->vdev_id);
+}
+
+static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       mutex_lock(&ar->conf_mutex);
+       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           ath10k_set_rts_iter, &ar_iter);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ar_iter.ret;
+}
+
+static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct ath10k_generic_iter *ar_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
+       int ret;
+
+       frag = clamp_t(u32, frag,
+                      ATH10K_FRAGMT_THRESHOLD_MIN,
+                      ATH10K_FRAGMT_THRESHOLD_MAX);
+
+       ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+                                       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+                                       frag);
+
+       ar_iter->ret = ret;
+       if (ar_iter->ret)
+               ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
+                           arvif->vdev_id);
+       else
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "Set frag threshold: %d for VDEV: %d\n",
+                          frag, arvif->vdev_id);
+}
+
+static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct ath10k_generic_iter ar_iter;
+       struct ath10k *ar = hw->priv;
+
+       memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+       ar_iter.ar = ar;
+
+       mutex_lock(&ar->conf_mutex);
+       ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+                                           ath10k_set_frag_iter, &ar_iter);
+       mutex_unlock(&ar->conf_mutex);
+
+       return ar_iter.ret;
+}
+
+static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       /* mac80211 doesn't care if we really xmit queued frames or not
+        * we'll collect those frames either way if we stop/delete vdevs */
+       if (drop)
+               return;
+
+       ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+                       bool empty;
+                       spin_lock_bh(&ar->htt->tx_lock);
+                       empty = bitmap_empty(ar->htt->used_msdu_ids,
+                                            ar->htt->max_num_pending_tx);
+                       spin_unlock_bh(&ar->htt->tx_lock);
+                       (empty);
+               }), ATH10K_FLUSH_TIMEOUT_HZ);
+       if (ret <= 0)
+               ath10k_warn("tx not flushed\n");
+}
+
+/* TODO: Implement this function properly
+ * For now it is needed to reply to Probe Requests in IBSS mode.
+ * Propably we need this information from FW.
+ */
+static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
+{
+       return 1;
+}
+
+static const struct ieee80211_ops ath10k_ops = {
+       .tx                             = ath10k_tx,
+       .start                          = ath10k_start,
+       .stop                           = ath10k_stop,
+       .config                         = ath10k_config,
+       .add_interface                  = ath10k_add_interface,
+       .remove_interface               = ath10k_remove_interface,
+       .configure_filter               = ath10k_configure_filter,
+       .bss_info_changed               = ath10k_bss_info_changed,
+       .hw_scan                        = ath10k_hw_scan,
+       .cancel_hw_scan                 = ath10k_cancel_hw_scan,
+       .set_key                        = ath10k_set_key,
+       .sta_state                      = ath10k_sta_state,
+       .conf_tx                        = ath10k_conf_tx,
+       .remain_on_channel              = ath10k_remain_on_channel,
+       .cancel_remain_on_channel       = ath10k_cancel_remain_on_channel,
+       .set_rts_threshold              = ath10k_set_rts_threshold,
+       .set_frag_threshold             = ath10k_set_frag_threshold,
+       .flush                          = ath10k_flush,
+       .tx_last_beacon                 = ath10k_tx_last_beacon,
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+       .bitrate                = (_rate), \
+       .flags                  = (_flags), \
+       .hw_value               = (_rateid), \
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+       .band                   = IEEE80211_BAND_2GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
+#define CHAN5G(_channel, _freq, _flags) { \
+       .band                   = IEEE80211_BAND_5GHZ, \
+       .hw_value               = (_channel), \
+       .center_freq            = (_freq), \
+       .flags                  = (_flags), \
+       .max_antenna_gain       = 0, \
+       .max_power              = 30, \
+}
+
+static const struct ieee80211_channel ath10k_2ghz_channels[] = {
+       CHAN2G(1, 2412, 0),
+       CHAN2G(2, 2417, 0),
+       CHAN2G(3, 2422, 0),
+       CHAN2G(4, 2427, 0),
+       CHAN2G(5, 2432, 0),
+       CHAN2G(6, 2437, 0),
+       CHAN2G(7, 2442, 0),
+       CHAN2G(8, 2447, 0),
+       CHAN2G(9, 2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0),
+};
+
+static const struct ieee80211_channel ath10k_5ghz_channels[] = {
+       CHAN5G(36, 5180, 14),
+       CHAN5G(40, 5200, 15),
+       CHAN5G(44, 5220, 16),
+       CHAN5G(48, 5240, 17),
+       CHAN5G(52, 5260, 18),
+       CHAN5G(56, 5280, 19),
+       CHAN5G(60, 5300, 20),
+       CHAN5G(64, 5320, 21),
+       CHAN5G(100, 5500, 22),
+       CHAN5G(104, 5520, 23),
+       CHAN5G(108, 5540, 24),
+       CHAN5G(112, 5560, 25),
+       CHAN5G(116, 5580, 26),
+       CHAN5G(120, 5600, 27),
+       CHAN5G(124, 5620, 28),
+       CHAN5G(128, 5640, 29),
+       CHAN5G(132, 5660, 30),
+       CHAN5G(136, 5680, 31),
+       CHAN5G(140, 5700, 32),
+       CHAN5G(149, 5745, 33),
+       CHAN5G(153, 5765, 34),
+       CHAN5G(157, 5785, 35),
+       CHAN5G(161, 5805, 36),
+       CHAN5G(165, 5825, 37),
+};
+
+static struct ieee80211_rate ath10k_rates[] = {
+       /* CCK */
+       RATETAB_ENT(10,  0x82, 0),
+       RATETAB_ENT(20,  0x84, 0),
+       RATETAB_ENT(55,  0x8b, 0),
+       RATETAB_ENT(110, 0x96, 0),
+       /* OFDM */
+       RATETAB_ENT(60,  0x0c, 0),
+       RATETAB_ENT(90,  0x12, 0),
+       RATETAB_ENT(120, 0x18, 0),
+       RATETAB_ENT(180, 0x24, 0),
+       RATETAB_ENT(240, 0x30, 0),
+       RATETAB_ENT(360, 0x48, 0),
+       RATETAB_ENT(480, 0x60, 0),
+       RATETAB_ENT(540, 0x6c, 0),
+};
+
+#define ath10k_a_rates (ath10k_rates + 4)
+#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - 4)
+#define ath10k_g_rates (ath10k_rates + 0)
+#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))
+
+struct ath10k *ath10k_mac_create(void)
+{
+       struct ieee80211_hw *hw;
+       struct ath10k *ar;
+
+       hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops);
+       if (!hw)
+               return NULL;
+
+       ar = hw->priv;
+       ar->hw = hw;
+
+       return ar;
+}
+
+void ath10k_mac_destroy(struct ath10k *ar)
+{
+       ieee80211_free_hw(ar->hw);
+}
+
+static const struct ieee80211_iface_limit ath10k_if_limits[] = {
+       {
+       .max    = 8,
+       .types  = BIT(NL80211_IFTYPE_STATION)
+               | BIT(NL80211_IFTYPE_P2P_CLIENT)
+               | BIT(NL80211_IFTYPE_P2P_GO)
+               | BIT(NL80211_IFTYPE_AP)
+       }
+};
+
+static const struct ieee80211_iface_combination ath10k_if_comb = {
+       .limits = ath10k_if_limits,
+       .n_limits = ARRAY_SIZE(ath10k_if_limits),
+       .max_interfaces = 8,
+       .num_different_channels = 1,
+       .beacon_int_infra_match = true,
+};
+
+static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
+{
+       struct ieee80211_sta_vht_cap vht_cap = {0};
+       u16 mcs_map;
+
+       vht_cap.vht_supported = 1;
+       vht_cap.cap = ar->vht_cap_info;
+
+       /* FIXME: check dynamically how many streams board supports */
+       mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
+               IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
+               IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
+               IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+
+       vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+       vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+
+       return vht_cap;
+}
+
+static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
+{
+       int i;
+       struct ieee80211_sta_ht_cap ht_cap = {0};
+
+       if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
+               return ht_cap;
+
+       ht_cap.ht_supported = 1;
+       ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+       ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+       ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
+               ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
+               u32 smps;
+
+               smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
+               smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+               ht_cap.cap |= smps;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC)
+               ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
+               u32 stbc;
+
+               stbc   = ar->ht_cap_info;
+               stbc  &= WMI_HT_CAP_RX_STBC;
+               stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
+               stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
+               stbc  &= IEEE80211_HT_CAP_RX_STBC;
+
+               ht_cap.cap |= stbc;
+       }
+
+       if (ar->ht_cap_info & WMI_HT_CAP_LDPC)
+               ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
+               ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
+
+       /* max AMSDU is implicitly taken from vht_cap_info */
+       if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
+               ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+       for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+               ht_cap.mcs.rx_mask[i] = 0xFF;
+
+       ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+
+       return ht_cap;
+}
+
+
+static void ath10k_get_arvif_iter(void *data, u8 *mac,
+                                 struct ieee80211_vif *vif)
+{
+       struct ath10k_vif_iter *arvif_iter = data;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       if (arvif->vdev_id == arvif_iter->vdev_id)
+               arvif_iter->arvif = arvif;
+}
+
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
+{
+       struct ath10k_vif_iter arvif_iter;
+       u32 flags;
+
+       memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter));
+       arvif_iter.vdev_id = vdev_id;
+
+       flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+       ieee80211_iterate_active_interfaces_atomic(ar->hw,
+                                                  flags,
+                                                  ath10k_get_arvif_iter,
+                                                  &arvif_iter);
+       if (!arvif_iter.arvif) {
+               ath10k_warn("No VIF found for VDEV: %d\n", vdev_id);
+               return NULL;
+       }
+
+       return arvif_iter.arvif;
+}
+
+int ath10k_mac_register(struct ath10k *ar)
+{
+       struct ieee80211_supported_band *band;
+       struct ieee80211_sta_vht_cap vht_cap;
+       struct ieee80211_sta_ht_cap ht_cap;
+       void *channels;
+       int ret;
+
+       SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
+
+       SET_IEEE80211_DEV(ar->hw, ar->dev);
+
+       ht_cap = ath10k_get_ht_cap(ar);
+       vht_cap = ath10k_create_vht_cap(ar);
+
+       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+               channels = kmemdup(ath10k_2ghz_channels,
+                                  sizeof(ath10k_2ghz_channels),
+                                  GFP_KERNEL);
+               if (!channels)
+                       return -ENOMEM;
+
+               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+               band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
+               band->channels = channels;
+               band->n_bitrates = ath10k_g_rates_size;
+               band->bitrates = ath10k_g_rates;
+               band->ht_cap = ht_cap;
+
+               /* vht is not supported in 2.4 GHz */
+
+               ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+       }
+
+       if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
+               channels = kmemdup(ath10k_5ghz_channels,
+                                  sizeof(ath10k_5ghz_channels),
+                                  GFP_KERNEL);
+               if (!channels) {
+                       if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+                               band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+                               kfree(band->channels);
+                       }
+                       return -ENOMEM;
+               }
+
+               band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
+               band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels);
+               band->channels = channels;
+               band->n_bitrates = ath10k_a_rates_size;
+               band->bitrates = ath10k_a_rates;
+               band->ht_cap = ht_cap;
+               band->vht_cap = vht_cap;
+               ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+       }
+
+       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);
+
+       ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                       IEEE80211_HW_SUPPORTS_PS |
+                       IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+                       IEEE80211_HW_SUPPORTS_UAPSD |
+                       IEEE80211_HW_MFP_CAPABLE |
+                       IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                       IEEE80211_HW_HAS_RATE_CONTROL |
+                       IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+                       IEEE80211_HW_WANT_MONITOR_VIF |
+                       IEEE80211_HW_AP_LINK_PS;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+               ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
+
+       if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
+               ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+               ar->hw->flags |= IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
+       }
+
+       ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID;
+       ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
+
+       ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+
+       ar->hw->channel_change_time = 5000;
+       ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
+
+       ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       ar->hw->wiphy->max_remain_on_channel_duration = 5000;
+
+       ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+       /*
+        * on LL hardware queues are managed entirely by the FW
+        * so we only advertise to mac we can do the queues thing
+        */
+       ar->hw->queues = 4;
+
+       ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
+       ar->hw->wiphy->n_iface_combinations = 1;
+
+       ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
+                           ath10k_reg_notifier);
+       if (ret) {
+               ath10k_err("Regulatory initialization failed\n");
+               return ret;
+       }
+
+       ret = ieee80211_register_hw(ar->hw);
+       if (ret) {
+               ath10k_err("ieee80211 registration failed: %d\n", ret);
+               return ret;
+       }
+
+       if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
+               ret = regulatory_hint(ar->hw->wiphy,
+                                     ar->ath_common.regulatory.alpha2);
+               if (ret)
+                       goto exit;
+       }
+
+       return 0;
+exit:
+       ieee80211_unregister_hw(ar->hw);
+       return ret;
+}
+
+void ath10k_mac_unregister(struct ath10k *ar)
+{
+       ieee80211_unregister_hw(ar->hw);
+
+       kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+       kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
+       SET_IEEE80211_DEV(ar->hw, NULL);
+}
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
new file mode 100644 (file)
index 0000000..27fc92e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _MAC_H_
+#define _MAC_H_
+
+#include <net/mac80211.h>
+#include "core.h"
+
+struct ath10k_generic_iter {
+       struct ath10k *ar;
+       int ret;
+};
+
+struct ath10k *ath10k_mac_create(void);
+void ath10k_mac_destroy(struct ath10k *ar);
+int ath10k_mac_register(struct ath10k *ar);
+void ath10k_mac_unregister(struct ath10k *ar);
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
+void ath10k_reset_scan(unsigned long ptr);
+void ath10k_offchan_tx_purge(struct ath10k *ar);
+void ath10k_offchan_tx_work(struct work_struct *work);
+
+static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
+{
+       return (struct ath10k_vif *)vif->drv_priv;
+}
+
+static inline void ath10k_tx_h_seq_no(struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+       if (info->flags  & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+               if (arvif->tx_seq_no == 0)
+                       arvif->tx_seq_no = 0x1000;
+
+               if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+                       arvif->tx_seq_no += 0x10;
+               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+               hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no);
+       }
+}
+
+#endif /* _MAC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
new file mode 100644 (file)
index 0000000..c8e9056
--- /dev/null
@@ -0,0 +1,2506 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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/pci.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "core.h"
+#include "debug.h"
+
+#include "targaddrs.h"
+#include "bmi.h"
+
+#include "hif.h"
+#include "htc.h"
+
+#include "ce.h"
+#include "pci.h"
+
+unsigned int ath10k_target_ps;
+module_param(ath10k_target_ps, uint, 0644);
+MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
+
+#define QCA988X_1_0_DEVICE_ID  (0xabcd)
+#define QCA988X_2_0_DEVICE_ID  (0x003c)
+
+static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
+       { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */
+       { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
+       {0}
+};
+
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+                                      u32 *data);
+
+static void ath10k_pci_process_ce(struct ath10k *ar);
+static int ath10k_pci_post_rx(struct ath10k *ar);
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+                                            int num);
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
+static void ath10k_pci_stop_ce(struct ath10k *ar);
+
+static const struct ce_attr host_ce_config_wlan[] = {
+       /* host->target HTC control and raw streams */
+       { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+       /* could be moved to share CE3 */
+       /* target->host HTT + HTC control */
+       { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,},
+       /* target->host WMI */
+       { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
+       /* host->target WMI */
+       { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
+       /* host->target HTT */
+       { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0,
+                   CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
+       /* unused */
+       { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+       /* Target autonomous hif_memcpy */
+       { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+       /* ce_diag, the Diagnostic Window */
+       { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+/* Target firmware's Copy Engine configuration. */
+static const struct ce_pipe_config target_ce_config_wlan[] = {
+       /* host->target HTC control and raw streams */
+       { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
+       /* target->host HTT + HTC control */
+       { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,},
+       /* target->host WMI */
+       { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* host->target WMI */
+       { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* host->target HTT */
+       { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+       /* NB: 50% of src nentries, since tx has 2 frags */
+       /* unused */
+       { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+       /* Reserved for target autonomous hif_memcpy */
+       { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+       /* CE7 used only by Host */
+};
+
+/*
+ * Diagnostic read/write access is provided for startup/config/debug usage.
+ * Caller must guarantee proper alignment, when applicable, and single user
+ * at any moment.
+ */
+static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
+                                   int nbytes)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 buf;
+       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int id;
+       unsigned int flags;
+       struct ce_state *ce_diag;
+       /* Host buffer address in CE space */
+       u32 ce_data;
+       dma_addr_t ce_data_base = 0;
+       void *data_buf = NULL;
+       int i;
+
+       /*
+        * This code cannot handle reads to non-memory space. Redirect to the
+        * register read fn but preserve the multi word read capability of
+        * this fn
+        */
+       if (address < DRAM_BASE_ADDRESS) {
+               if (!IS_ALIGNED(address, 4) ||
+                   !IS_ALIGNED((unsigned long)data, 4))
+                       return -EIO;
+
+               while ((nbytes >= 4) &&  ((ret = ath10k_pci_diag_read_access(
+                                          ar, address, (u32 *)data)) == 0)) {
+                       nbytes -= sizeof(u32);
+                       address += sizeof(u32);
+                       data += sizeof(u32);
+               }
+               return ret;
+       }
+
+       ce_diag = ar_pci->ce_diag;
+
+       /*
+        * Allocate a temporary bounce buffer to hold caller's data
+        * to be DMA'ed from Target. This guarantees
+        *   1) 4-byte alignment
+        *   2) Buffer in DMA-able space
+        */
+       orig_nbytes = nbytes;
+       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+                                                        orig_nbytes,
+                                                        &ce_data_base);
+
+       if (!data_buf) {
+               ret = -ENOMEM;
+               goto done;
+       }
+       memset(data_buf, 0, orig_nbytes);
+
+       remaining_bytes = orig_nbytes;
+       ce_data = ce_data_base;
+       while (remaining_bytes) {
+               nbytes = min_t(unsigned int, remaining_bytes,
+                              DIAG_TRANSFER_LIMIT);
+
+               ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data);
+               if (ret != 0)
+                       goto done;
+
+               /* Request CE to send from Target(!) address to Host buffer */
+               /*
+                * The address supplied by the caller is in the
+                * Target CPU virtual address space.
+                *
+                * In order to use this address with the diagnostic CE,
+                * convert it from Target CPU virtual address space
+                * to CE address space
+                */
+               ath10k_pci_wake(ar);
+               address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
+                                                    address);
+               ath10k_pci_sleep(ar);
+
+               ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
+                                0);
+               if (ret)
+                       goto done;
+
+               i = 0;
+               while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id) != 0) {
+                       mdelay(1);
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != (u32) address) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               i = 0;
+               while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id, &flags) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != ce_data) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               remaining_bytes -= nbytes;
+               address += nbytes;
+               ce_data += nbytes;
+       }
+
+done:
+       if (ret == 0) {
+               /* Copy data from allocated DMA buf to caller's buf */
+               WARN_ON_ONCE(orig_nbytes & 3);
+               for (i = 0; i < orig_nbytes / sizeof(__le32); i++) {
+                       ((u32 *)data)[i] =
+                               __le32_to_cpu(((__le32 *)data_buf)[i]);
+               }
+       } else
+               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n",
+                          __func__, address);
+
+       if (data_buf)
+               pci_free_consistent(ar_pci->pdev, orig_nbytes,
+                                   data_buf, ce_data_base);
+
+       return ret;
+}
+
+/* Read 4-byte aligned data from Target memory or register */
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+                                      u32 *data)
+{
+       /* Assume range doesn't cross this boundary */
+       if (address >= DRAM_BASE_ADDRESS)
+               return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32));
+
+       ath10k_pci_wake(ar);
+       *data = ath10k_pci_read32(ar, address);
+       ath10k_pci_sleep(ar);
+       return 0;
+}
+
+static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+                                    const void *data, int nbytes)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 buf;
+       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int id;
+       unsigned int flags;
+       struct ce_state *ce_diag;
+       void *data_buf = NULL;
+       u32 ce_data;    /* Host buffer address in CE space */
+       dma_addr_t ce_data_base = 0;
+       int i;
+
+       ce_diag = ar_pci->ce_diag;
+
+       /*
+        * Allocate a temporary bounce buffer to hold caller's data
+        * to be DMA'ed to Target. This guarantees
+        *   1) 4-byte alignment
+        *   2) Buffer in DMA-able space
+        */
+       orig_nbytes = nbytes;
+       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+                                                        orig_nbytes,
+                                                        &ce_data_base);
+       if (!data_buf) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       /* Copy caller's data to allocated DMA buf */
+       WARN_ON_ONCE(orig_nbytes & 3);
+       for (i = 0; i < orig_nbytes / sizeof(__le32); i++)
+               ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]);
+
+       /*
+        * The address supplied by the caller is in the
+        * Target CPU virtual address space.
+        *
+        * In order to use this address with the diagnostic CE,
+        * convert it from
+        *    Target CPU virtual address space
+        * to
+        *    CE address space
+        */
+       ath10k_pci_wake(ar);
+       address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address);
+       ath10k_pci_sleep(ar);
+
+       remaining_bytes = orig_nbytes;
+       ce_data = ce_data_base;
+       while (remaining_bytes) {
+               /* FIXME: check cast */
+               nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
+
+               /* Set up to receive directly into Target(!) address */
+               ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address);
+               if (ret != 0)
+                       goto done;
+
+               /*
+                * Request CE to send caller-supplied data that
+                * was copied to bounce buffer to Target(!) address.
+                */
+               ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data,
+                                    nbytes, 0, 0);
+               if (ret != 0)
+                       goto done;
+
+               i = 0;
+               while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != ce_data) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               i = 0;
+               while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+                                                    &completed_nbytes,
+                                                    &id, &flags) != 0) {
+                       mdelay(1);
+
+                       if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+                               ret = -EBUSY;
+                               goto done;
+                       }
+               }
+
+               if (nbytes != completed_nbytes) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               if (buf != address) {
+                       ret = -EIO;
+                       goto done;
+               }
+
+               remaining_bytes -= nbytes;
+               address += nbytes;
+               ce_data += nbytes;
+       }
+
+done:
+       if (data_buf) {
+               pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf,
+                                   ce_data_base);
+       }
+
+       if (ret != 0)
+               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__,
+                          address);
+
+       return ret;
+}
+
+/* Write 4B data to Target memory or register */
+static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address,
+                                       u32 data)
+{
+       /* Assume range doesn't cross this boundary */
+       if (address >= DRAM_BASE_ADDRESS)
+               return ath10k_pci_diag_write_mem(ar, address, &data,
+                                                sizeof(u32));
+
+       ath10k_pci_wake(ar);
+       ath10k_pci_write32(ar, address, data);
+       ath10k_pci_sleep(ar);
+       return 0;
+}
+
+static bool ath10k_pci_target_is_awake(struct ath10k *ar)
+{
+       void __iomem *mem = ath10k_pci_priv(ar)->mem;
+       u32 val;
+       val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS +
+                      RTC_STATE_ADDRESS);
+       return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON);
+}
+
+static void ath10k_pci_wait(struct ath10k *ar)
+{
+       int n = 100;
+
+       while (n-- && !ath10k_pci_target_is_awake(ar))
+               msleep(10);
+
+       if (n < 0)
+               ath10k_warn("Unable to wakeup target\n");
+}
+
+void ath10k_do_pci_wake(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *pci_addr = ar_pci->mem;
+       int tot_delay = 0;
+       int curr_delay = 5;
+
+       if (atomic_read(&ar_pci->keep_awake_count) == 0) {
+               /* Force AWAKE */
+               iowrite32(PCIE_SOC_WAKE_V_MASK,
+                         pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+       }
+       atomic_inc(&ar_pci->keep_awake_count);
+
+       if (ar_pci->verified_awake)
+               return;
+
+       for (;;) {
+               if (ath10k_pci_target_is_awake(ar)) {
+                       ar_pci->verified_awake = true;
+                       break;
+               }
+
+               if (tot_delay > PCIE_WAKE_TIMEOUT) {
+                       ath10k_warn("target takes too long to wake up (awake count %d)\n",
+                                   atomic_read(&ar_pci->keep_awake_count));
+                       break;
+               }
+
+               udelay(curr_delay);
+               tot_delay += curr_delay;
+
+               if (curr_delay < 50)
+                       curr_delay += 5;
+       }
+}
+
+void ath10k_do_pci_sleep(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *pci_addr = ar_pci->mem;
+
+       if (atomic_dec_and_test(&ar_pci->keep_awake_count)) {
+               /* Allow sleep */
+               ar_pci->verified_awake = false;
+               iowrite32(PCIE_SOC_WAKE_RESET,
+                         pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+       }
+}
+
+/*
+ * FIXME: Handle OOM properly.
+ */
+static inline
+struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k_pci_compl *compl = NULL;
+
+       spin_lock_bh(&pipe_info->pipe_lock);
+       if (list_empty(&pipe_info->compl_free)) {
+               ath10k_warn("Completion buffers are full\n");
+               goto exit;
+       }
+       compl = list_first_entry(&pipe_info->compl_free,
+                                struct ath10k_pci_compl, list);
+       list_del(&compl->list);
+exit:
+       spin_unlock_bh(&pipe_info->pipe_lock);
+       return compl;
+}
+
+/* Called by lower (CE) layer when a send to Target completes. */
+static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
+                                   void *transfer_context,
+                                   u32 ce_data,
+                                   unsigned int nbytes,
+                                   unsigned int transfer_id)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_compl *compl;
+       bool process = false;
+
+       do {
+               /*
+                * For the send completion of an item in sendlist, just
+                * increment num_sends_allowed. The upper layer callback will
+                * be triggered when last fragment is done with send.
+                */
+               if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
+                       spin_lock_bh(&pipe_info->pipe_lock);
+                       pipe_info->num_sends_allowed++;
+                       spin_unlock_bh(&pipe_info->pipe_lock);
+                       continue;
+               }
+
+               compl = get_free_compl(pipe_info);
+               if (!compl)
+                       break;
+
+               compl->send_or_recv = HIF_CE_COMPLETE_SEND;
+               compl->ce_state = ce_state;
+               compl->pipe_info = pipe_info;
+               compl->transfer_context = transfer_context;
+               compl->nbytes = nbytes;
+               compl->transfer_id = transfer_id;
+               compl->flags = 0;
+
+               /*
+                * Add the completion to the processing queue.
+                */
+               spin_lock_bh(&ar_pci->compl_lock);
+               list_add_tail(&compl->list, &ar_pci->compl_process);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+               process = true;
+       } while (ath10k_ce_completed_send_next(ce_state,
+                                                          &transfer_context,
+                                                          &ce_data, &nbytes,
+                                                          &transfer_id) == 0);
+
+       /*
+        * If only some of the items within a sendlist have completed,
+        * don't invoke completion processing until the entire sendlist
+        * has been sent.
+        */
+       if (!process)
+               return;
+
+       ath10k_pci_process_ce(ar);
+}
+
+/* Called by lower (CE) layer when data is received from the Target. */
+static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
+                                   void *transfer_context, u32 ce_data,
+                                   unsigned int nbytes,
+                                   unsigned int transfer_id,
+                                   unsigned int flags)
+{
+       struct ath10k *ar = ce_state->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+
+       do {
+               compl = get_free_compl(pipe_info);
+               if (!compl)
+                       break;
+
+               compl->send_or_recv = HIF_CE_COMPLETE_RECV;
+               compl->ce_state = ce_state;
+               compl->pipe_info = pipe_info;
+               compl->transfer_context = transfer_context;
+               compl->nbytes = nbytes;
+               compl->transfer_id = transfer_id;
+               compl->flags = flags;
+
+               skb = transfer_context;
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               /*
+                * Add the completion to the processing queue.
+                */
+               spin_lock_bh(&ar_pci->compl_lock);
+               list_add_tail(&compl->list, &ar_pci->compl_process);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+       } while (ath10k_ce_completed_recv_next(ce_state,
+                                                          &transfer_context,
+                                                          &ce_data, &nbytes,
+                                                          &transfer_id,
+                                                          &flags) == 0);
+
+       ath10k_pci_process_ce(ar);
+}
+
+/* Send the first nbytes bytes of the buffer */
+static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
+                                   unsigned int transfer_id,
+                                   unsigned int bytes, struct sk_buff *nbuf)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]);
+       struct ce_state *ce_hdl = pipe_info->ce_hdl;
+       struct ce_sendlist sendlist;
+       unsigned int len;
+       u32 flags = 0;
+       int ret;
+
+       memset(&sendlist, 0, sizeof(struct ce_sendlist));
+
+       len = min(bytes, nbuf->len);
+       bytes -= len;
+
+       if (len & 3)
+               ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len);
+
+       ath10k_dbg(ATH10K_DBG_PCI,
+                  "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n",
+                  nbuf->data, (unsigned long long) skb_cb->paddr,
+                  nbuf->len, len);
+       ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+                       "ath10k tx: data: ",
+                       nbuf->data, nbuf->len);
+
+       ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
+
+       /* Make sure we have resources to handle this request */
+       spin_lock_bh(&pipe_info->pipe_lock);
+       if (!pipe_info->num_sends_allowed) {
+               ath10k_warn("Pipe: %d is full\n", pipe_id);
+               spin_unlock_bh(&pipe_info->pipe_lock);
+               return -ENOSR;
+       }
+       pipe_info->num_sends_allowed--;
+       spin_unlock_bh(&pipe_info->pipe_lock);
+
+       ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
+       if (ret)
+               ath10k_warn("CE send failed: %p\n", nbuf);
+
+       return ret;
+}
+
+static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]);
+       int ret;
+
+       spin_lock_bh(&pipe_info->pipe_lock);
+       ret = pipe_info->num_sends_allowed;
+       spin_unlock_bh(&pipe_info->pipe_lock);
+
+       return ret;
+}
+
+static void ath10k_pci_hif_dump_area(struct ath10k *ar)
+{
+       u32 reg_dump_area = 0;
+       u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
+       u32 host_addr;
+       int ret;
+       u32 i;
+
+       ath10k_err("firmware crashed!\n");
+       ath10k_err("hardware name %s version 0x%x\n",
+                  ar->hw_params.name, ar->target_version);
+       ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
+                  ar->fw_version_minor, ar->fw_version_release,
+                  ar->fw_version_build);
+
+       host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
+       if (ath10k_pci_diag_read_mem(ar, host_addr,
+                                    &reg_dump_area, sizeof(u32)) != 0) {
+               ath10k_warn("could not read hi_failure_state\n");
+               return;
+       }
+
+       ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area);
+
+       ret = ath10k_pci_diag_read_mem(ar, reg_dump_area,
+                                      &reg_dump_values[0],
+                                      REG_DUMP_COUNT_QCA988X * sizeof(u32));
+       if (ret != 0) {
+               ath10k_err("could not dump FW Dump Area\n");
+               return;
+       }
+
+       BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4);
+
+       ath10k_err("target Register Dump\n");
+       for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4)
+               ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
+                          i,
+                          reg_dump_values[i],
+                          reg_dump_values[i + 1],
+                          reg_dump_values[i + 2],
+                          reg_dump_values[i + 3]);
+}
+
+static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+                                              int force)
+{
+       if (!force) {
+               int resources;
+               /*
+                * Decide whether to actually poll for completions, or just
+                * wait for a later chance.
+                * If there seem to be plenty of resources left, then just wait
+                * since checking involves reading a CE register, which is a
+                * relatively expensive operation.
+                */
+               resources = ath10k_pci_hif_get_free_queue_number(ar, pipe);
+
+               /*
+                * If at least 50% of the total resources are still available,
+                * don't bother checking again yet.
+                */
+               if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1))
+                       return;
+       }
+       ath10k_ce_per_engine_service(ar, pipe);
+}
+
+static void ath10k_pci_hif_post_init(struct ath10k *ar,
+                                    struct ath10k_hif_cb *callbacks)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       memcpy(&ar_pci->msg_callbacks_current, callbacks,
+              sizeof(ar_pci->msg_callbacks_current));
+}
+
+static int ath10k_pci_start_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_diag = ar_pci->ce_diag;
+       const struct ce_attr *attr;
+       struct hif_ce_pipe_info *pipe_info;
+       struct ath10k_pci_compl *compl;
+       int i, pipe_num, completions, disable_interrupts;
+
+       spin_lock_init(&ar_pci->compl_lock);
+       INIT_LIST_HEAD(&ar_pci->compl_process);
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+
+               spin_lock_init(&pipe_info->pipe_lock);
+               INIT_LIST_HEAD(&pipe_info->compl_free);
+
+               /* Handle Diagnostic CE specially */
+               if (pipe_info->ce_hdl == ce_diag)
+                       continue;
+
+               attr = &host_ce_config_wlan[pipe_num];
+               completions = 0;
+
+               if (attr->src_nentries) {
+                       disable_interrupts = attr->flags & CE_ATTR_DIS_INTR;
+                       ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+                                                  ath10k_pci_ce_send_done,
+                                                  disable_interrupts);
+                       completions += attr->src_nentries;
+                       pipe_info->num_sends_allowed = attr->src_nentries - 1;
+               }
+
+               if (attr->dest_nentries) {
+                       ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+                                                  ath10k_pci_ce_recv_data);
+                       completions += attr->dest_nentries;
+               }
+
+               if (completions == 0)
+                       continue;
+
+               for (i = 0; i < completions; i++) {
+                       compl = kmalloc(sizeof(struct ath10k_pci_compl),
+                                       GFP_KERNEL);
+                       if (!compl) {
+                               ath10k_warn("No memory for completion state\n");
+                               ath10k_pci_stop_ce(ar);
+                               return -ENOMEM;
+                       }
+
+                       compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+                       list_add_tail(&compl->list, &pipe_info->compl_free);
+               }
+       }
+
+       return 0;
+}
+
+static void ath10k_pci_stop_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+       int i;
+
+       ath10k_ce_disable_interrupts(ar);
+
+       /* Cancel the pending tasklet */
+       tasklet_kill(&ar_pci->intr_tq);
+
+       for (i = 0; i < CE_COUNT; i++)
+               tasklet_kill(&ar_pci->pipe_info[i].intr);
+
+       /* Mark pending completions as aborted, so that upper layers free up
+        * their associated resources */
+       spin_lock_bh(&ar_pci->compl_lock);
+       list_for_each_entry(compl, &ar_pci->compl_process, list) {
+               skb = (struct sk_buff *)compl->transfer_context;
+               ATH10K_SKB_CB(skb)->is_aborted = true;
+       }
+       spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+static void ath10k_pci_cleanup_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_pci_compl *compl, *tmp;
+       struct hif_ce_pipe_info *pipe_info;
+       struct sk_buff *netbuf;
+       int pipe_num;
+
+       /* Free pending completions. */
+       spin_lock_bh(&ar_pci->compl_lock);
+       if (!list_empty(&ar_pci->compl_process))
+               ath10k_warn("pending completions still present! possible memory leaks.\n");
+
+       list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
+               list_del(&compl->list);
+               netbuf = (struct sk_buff *)compl->transfer_context;
+               dev_kfree_skb_any(netbuf);
+               kfree(compl);
+       }
+       spin_unlock_bh(&ar_pci->compl_lock);
+
+       /* Free unused completions for each pipe. */
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+
+               spin_lock_bh(&pipe_info->pipe_lock);
+               list_for_each_entry_safe(compl, tmp,
+                                        &pipe_info->compl_free, list) {
+                       list_del(&compl->list);
+                       kfree(compl);
+               }
+               spin_unlock_bh(&pipe_info->pipe_lock);
+       }
+}
+
+static void ath10k_pci_process_ce(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ar->hif.priv;
+       struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+       struct ath10k_pci_compl *compl;
+       struct sk_buff *skb;
+       unsigned int nbytes;
+       int ret, send_done = 0;
+
+       /* Upper layers aren't ready to handle tx/rx completions in parallel so
+        * we must serialize all completion processing. */
+
+       spin_lock_bh(&ar_pci->compl_lock);
+       if (ar_pci->compl_processing) {
+               spin_unlock_bh(&ar_pci->compl_lock);
+               return;
+       }
+       ar_pci->compl_processing = true;
+       spin_unlock_bh(&ar_pci->compl_lock);
+
+       for (;;) {
+               spin_lock_bh(&ar_pci->compl_lock);
+               if (list_empty(&ar_pci->compl_process)) {
+                       spin_unlock_bh(&ar_pci->compl_lock);
+                       break;
+               }
+               compl = list_first_entry(&ar_pci->compl_process,
+                                        struct ath10k_pci_compl, list);
+               list_del(&compl->list);
+               spin_unlock_bh(&ar_pci->compl_lock);
+
+               if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
+                       cb->tx_completion(ar,
+                                         compl->transfer_context,
+                                         compl->transfer_id);
+                       send_done = 1;
+               } else {
+                       ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
+                       if (ret) {
+                               ath10k_warn("Unable to post recv buffer for pipe: %d\n",
+                                           compl->pipe_info->pipe_num);
+                               break;
+                       }
+
+                       skb = (struct sk_buff *)compl->transfer_context;
+                       nbytes = compl->nbytes;
+
+                       ath10k_dbg(ATH10K_DBG_PCI,
+                                  "ath10k_pci_ce_recv_data netbuf=%p  nbytes=%d\n",
+                                  skb, nbytes);
+                       ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+                                       "ath10k rx: ", skb->data, nbytes);
+
+                       if (skb->len + skb_tailroom(skb) >= nbytes) {
+                               skb_trim(skb, 0);
+                               skb_put(skb, nbytes);
+                               cb->rx_completion(ar, skb,
+                                                 compl->pipe_info->pipe_num);
+                       } else {
+                               ath10k_warn("rxed more than expected (nbytes %d, max %d)",
+                                           nbytes,
+                                           skb->len + skb_tailroom(skb));
+                       }
+               }
+
+               compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+
+               /*
+                * Add completion back to the pipe's free list.
+                */
+               spin_lock_bh(&compl->pipe_info->pipe_lock);
+               list_add_tail(&compl->list, &compl->pipe_info->compl_free);
+               compl->pipe_info->num_sends_allowed += send_done;
+               spin_unlock_bh(&compl->pipe_info->pipe_lock);
+       }
+
+       spin_lock_bh(&ar_pci->compl_lock);
+       ar_pci->compl_processing = false;
+       spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+/* TODO - temporary mapping while we have too few CE's */
+static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
+                                             u16 service_id, u8 *ul_pipe,
+                                             u8 *dl_pipe, int *ul_is_polled,
+                                             int *dl_is_polled)
+{
+       int ret = 0;
+
+       /* polling for received messages not supported */
+       *dl_is_polled = 0;
+
+       switch (service_id) {
+       case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+               /*
+                * Host->target HTT gets its own pipe, so it can be polled
+                * while other pipes are interrupt driven.
+                */
+               *ul_pipe = 4;
+               /*
+                * Use the same target->host pipe for HTC ctrl, HTC raw
+                * streams, and HTT.
+                */
+               *dl_pipe = 1;
+               break;
+
+       case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+       case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+               /*
+                * Note: HTC_RAW_STREAMS_SVC is currently unused, and
+                * HTC_CTRL_RSVD_SVC could share the same pipe as the
+                * WMI services.  So, if another CE is needed, change
+                * this to *ul_pipe = 3, which frees up CE 0.
+                */
+               /* *ul_pipe = 3; */
+               *ul_pipe = 0;
+               *dl_pipe = 1;
+               break;
+
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+       case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+
+       case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+               *ul_pipe = 3;
+               *dl_pipe = 2;
+               break;
+
+               /* pipe 5 unused   */
+               /* pipe 6 reserved */
+               /* pipe 7 reserved */
+
+       default:
+               ret = -1;
+               break;
+       }
+       *ul_is_polled =
+               (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0;
+
+       return ret;
+}
+
+static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
+                                               u8 *ul_pipe, u8 *dl_pipe)
+{
+       int ul_is_polled, dl_is_polled;
+
+       (void)ath10k_pci_hif_map_service_to_pipe(ar,
+                                                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                                                ul_pipe,
+                                                dl_pipe,
+                                                &ul_is_polled,
+                                                &dl_is_polled);
+}
+
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+                                  int num)
+{
+       struct ath10k *ar = pipe_info->hif_ce_state;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_state = pipe_info->ce_hdl;
+       struct sk_buff *skb;
+       dma_addr_t ce_data;
+       int i, ret = 0;
+
+       if (pipe_info->buf_sz == 0)
+               return 0;
+
+       for (i = 0; i < num; i++) {
+               skb = dev_alloc_skb(pipe_info->buf_sz);
+               if (!skb) {
+                       ath10k_warn("could not allocate skbuff for pipe %d\n",
+                                   num);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+               ce_data = dma_map_single(ar->dev, skb->data,
+                                        skb->len + skb_tailroom(skb),
+                                        DMA_FROM_DEVICE);
+
+               if (unlikely(dma_mapping_error(ar->dev, ce_data))) {
+                       ath10k_warn("could not dma map skbuff\n");
+                       dev_kfree_skb_any(skb);
+                       ret = -EIO;
+                       goto err;
+               }
+
+               ATH10K_SKB_CB(skb)->paddr = ce_data;
+
+               pci_dma_sync_single_for_device(ar_pci->pdev, ce_data,
+                                              pipe_info->buf_sz,
+                                              PCI_DMA_FROMDEVICE);
+
+               ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb,
+                                                ce_data);
+               if (ret) {
+                       ath10k_warn("could not enqueue to pipe %d (%d)\n",
+                                   num, ret);
+                       goto err;
+               }
+       }
+
+       return ret;
+
+err:
+       ath10k_pci_rx_pipe_cleanup(pipe_info);
+       return ret;
+}
+
+static int ath10k_pci_post_rx(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       const struct ce_attr *attr;
+       int pipe_num, ret = 0;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               attr = &host_ce_config_wlan[pipe_num];
+
+               if (attr->dest_nentries == 0)
+                       continue;
+
+               ret = ath10k_pci_post_rx_pipe(pipe_info,
+                                             attr->dest_nentries - 1);
+               if (ret) {
+                       ath10k_warn("Unable to replenish recv buffers for pipe: %d\n",
+                                   pipe_num);
+
+                       for (; pipe_num >= 0; pipe_num--) {
+                               pipe_info = &ar_pci->pipe_info[pipe_num];
+                               ath10k_pci_rx_pipe_cleanup(pipe_info);
+                       }
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int ath10k_pci_hif_start(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = ath10k_pci_start_ce(ar);
+       if (ret) {
+               ath10k_warn("could not start CE (%d)\n", ret);
+               return ret;
+       }
+
+       /* Post buffers once to start things off. */
+       ret = ath10k_pci_post_rx(ar);
+       if (ret) {
+               ath10k_warn("could not post rx pipes (%d)\n", ret);
+               return ret;
+       }
+
+       ar_pci->started = 1;
+       return 0;
+}
+
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       struct ce_state *ce_hdl;
+       u32 buf_sz;
+       struct sk_buff *netbuf;
+       u32 ce_data;
+
+       buf_sz = pipe_info->buf_sz;
+
+       /* Unused Copy Engine */
+       if (buf_sz == 0)
+               return;
+
+       ar = pipe_info->hif_ce_state;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci->started)
+               return;
+
+       ce_hdl = pipe_info->ce_hdl;
+
+       while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
+                                         &ce_data) == 0) {
+               dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
+                                netbuf->len + skb_tailroom(netbuf),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(netbuf);
+       }
+}
+
+static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       struct ce_state *ce_hdl;
+       struct sk_buff *netbuf;
+       u32 ce_data;
+       unsigned int nbytes;
+       unsigned int id;
+       u32 buf_sz;
+
+       buf_sz = pipe_info->buf_sz;
+
+       /* Unused Copy Engine */
+       if (buf_sz == 0)
+               return;
+
+       ar = pipe_info->hif_ce_state;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci->started)
+               return;
+
+       ce_hdl = pipe_info->ce_hdl;
+
+       while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
+                                         &ce_data, &nbytes, &id) == 0) {
+               if (netbuf != CE_SENDLIST_ITEM_CTXT)
+                       /*
+                        * Indicate the completion to higer layer to free
+                        * the buffer
+                        */
+                       ATH10K_SKB_CB(netbuf)->is_aborted = true;
+                       ar_pci->msg_callbacks_current.tx_completion(ar,
+                                                                   netbuf,
+                                                                   id);
+       }
+}
+
+/*
+ * Cleanup residual buffers for device shutdown:
+ *    buffers that were enqueued for receive
+ *    buffers that were to be sent
+ * Note: Buffers that had completed but which were
+ * not yet processed are on a completion queue. They
+ * are handled when the completion thread shuts down.
+ */
+static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               struct hif_ce_pipe_info *pipe_info;
+
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               ath10k_pci_rx_pipe_cleanup(pipe_info);
+               ath10k_pci_tx_pipe_cleanup(pipe_info);
+       }
+}
+
+static void ath10k_pci_ce_deinit(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               if (pipe_info->ce_hdl) {
+                       ath10k_ce_deinit(pipe_info->ce_hdl);
+                       pipe_info->ce_hdl = NULL;
+                       pipe_info->buf_sz = 0;
+               }
+       }
+}
+
+static void ath10k_pci_hif_stop(struct ath10k *ar)
+{
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       ath10k_pci_stop_ce(ar);
+
+       /* At this point, asynchronous threads are stopped, the target should
+        * not DMA nor interrupt. We process the leftovers and then free
+        * everything else up. */
+
+       ath10k_pci_process_ce(ar);
+       ath10k_pci_cleanup_ce(ar);
+       ath10k_pci_buffer_cleanup(ar);
+       ath10k_pci_ce_deinit(ar);
+}
+
+static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
+                                          void *req, u32 req_len,
+                                          void *resp, u32 *resp_len)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl;
+       struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl;
+       dma_addr_t req_paddr = 0;
+       dma_addr_t resp_paddr = 0;
+       struct bmi_xfer xfer = {};
+       void *treq, *tresp = NULL;
+       int ret = 0;
+
+       if (resp && !resp_len)
+               return -EINVAL;
+
+       if (resp && resp_len && *resp_len == 0)
+               return -EINVAL;
+
+       treq = kmemdup(req, req_len, GFP_KERNEL);
+       if (!treq)
+               return -ENOMEM;
+
+       req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE);
+       ret = dma_mapping_error(ar->dev, req_paddr);
+       if (ret)
+               goto err_dma;
+
+       if (resp && resp_len) {
+               tresp = kzalloc(*resp_len, GFP_KERNEL);
+               if (!tresp) {
+                       ret = -ENOMEM;
+                       goto err_req;
+               }
+
+               resp_paddr = dma_map_single(ar->dev, tresp, *resp_len,
+                                           DMA_FROM_DEVICE);
+               ret = dma_mapping_error(ar->dev, resp_paddr);
+               if (ret)
+                       goto err_req;
+
+               xfer.wait_for_resp = true;
+               xfer.resp_len = 0;
+
+               ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
+       }
+
+       init_completion(&xfer.done);
+
+       ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
+       if (ret)
+               goto err_resp;
+
+       ret = wait_for_completion_timeout(&xfer.done,
+                                         BMI_COMMUNICATION_TIMEOUT_HZ);
+       if (ret <= 0) {
+               u32 unused_buffer;
+               unsigned int unused_nbytes;
+               unsigned int unused_id;
+
+               ret = -ETIMEDOUT;
+               ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer,
+                                          &unused_nbytes, &unused_id);
+       } else {
+               /* non-zero means we did not time out */
+               ret = 0;
+       }
+
+err_resp:
+       if (resp) {
+               u32 unused_buffer;
+
+               ath10k_ce_revoke_recv_next(ce_rx, NULL, &unused_buffer);
+               dma_unmap_single(ar->dev, resp_paddr,
+                                *resp_len, DMA_FROM_DEVICE);
+       }
+err_req:
+       dma_unmap_single(ar->dev, req_paddr, req_len, DMA_TO_DEVICE);
+
+       if (ret == 0 && resp_len) {
+               *resp_len = min(*resp_len, xfer.resp_len);
+               memcpy(resp, tresp, xfer.resp_len);
+       }
+err_dma:
+       kfree(treq);
+       kfree(tresp);
+
+       return ret;
+}
+
+static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
+                                    void *transfer_context,
+                                    u32 data,
+                                    unsigned int nbytes,
+                                    unsigned int transfer_id)
+{
+       struct bmi_xfer *xfer = transfer_context;
+
+       if (xfer->wait_for_resp)
+               return;
+
+       complete(&xfer->done);
+}
+
+static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state,
+                                    void *transfer_context,
+                                    u32 data,
+                                    unsigned int nbytes,
+                                    unsigned int transfer_id,
+                                    unsigned int flags)
+{
+       struct bmi_xfer *xfer = transfer_context;
+
+       if (!xfer->wait_for_resp) {
+               ath10k_warn("unexpected: BMI data received; ignoring\n");
+               return;
+       }
+
+       xfer->resp_len = nbytes;
+       complete(&xfer->done);
+}
+
+/*
+ * Map from service/endpoint to Copy Engine.
+ * This table is derived from the CE_PCI TABLE, above.
+ * It is passed to the Target at startup for use by firmware.
+ */
+static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_CONTROL,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                3,
+       },
+       {
+                ATH10K_HTC_SVC_ID_WMI_CONTROL,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                2,
+       },
+       {
+                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                0,             /* could be moved to 3 (share with WMI) */
+       },
+       {
+                ATH10K_HTC_SVC_ID_RSVD_CTRL,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+       {
+                ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,    /* not currently used */
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                0,
+       },
+       {
+                ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,    /* not currently used */
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+       {
+                ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+                PIPEDIR_OUT,           /* out = UL = host -> target */
+                4,
+       },
+       {
+                ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+                PIPEDIR_IN,            /* in = DL = target -> host */
+                1,
+       },
+
+       /* (Additions here) */
+
+       {                               /* Must be last */
+                0,
+                0,
+                0,
+       },
+};
+
+/*
+ * Send an interrupt to the device to wake up the Target CPU
+ * so it has an opportunity to notice any changed state.
+ */
+static int ath10k_pci_wake_target_cpu(struct ath10k *ar)
+{
+       int ret;
+       u32 core_ctrl;
+
+       ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS |
+                                             CORE_CTRL_ADDRESS,
+                                         &core_ctrl);
+       if (ret) {
+               ath10k_warn("Unable to read core ctrl\n");
+               return ret;
+       }
+
+       /* A_INUM_FIRMWARE interrupt to Target CPU */
+       core_ctrl |= CORE_CTRL_CPU_INTR_MASK;
+
+       ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS |
+                                              CORE_CTRL_ADDRESS,
+                                          core_ctrl);
+       if (ret)
+               ath10k_warn("Unable to set interrupt mask\n");
+
+       return ret;
+}
+
+static int ath10k_pci_init_config(struct ath10k *ar)
+{
+       u32 interconnect_targ_addr;
+       u32 pcie_state_targ_addr = 0;
+       u32 pipe_cfg_targ_addr = 0;
+       u32 svc_to_pipe_map = 0;
+       u32 pcie_config_flags = 0;
+       u32 ealloc_value;
+       u32 ealloc_targ_addr;
+       u32 flag2_value;
+       u32 flag2_targ_addr;
+       int ret = 0;
+
+       /* Download to Target the CE Config and the service-to-CE map */
+       interconnect_targ_addr =
+               host_interest_item_address(HI_ITEM(hi_interconnect_state));
+
+       /* Supply Target-side CE configuration */
+       ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr,
+                                         &pcie_state_targ_addr);
+       if (ret != 0) {
+               ath10k_err("Failed to get pcie state addr: %d\n", ret);
+               return ret;
+       }
+
+       if (pcie_state_targ_addr == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid pcie state addr\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  pipe_cfg_addr),
+                                         &pipe_cfg_targ_addr);
+       if (ret != 0) {
+               ath10k_err("Failed to get pipe cfg addr: %d\n", ret);
+               return ret;
+       }
+
+       if (pipe_cfg_targ_addr == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid pipe cfg addr\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr,
+                                target_ce_config_wlan,
+                                sizeof(target_ce_config_wlan));
+
+       if (ret != 0) {
+               ath10k_err("Failed to write pipe cfg: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  svc_to_pipe_map),
+                                         &svc_to_pipe_map);
+       if (ret != 0) {
+               ath10k_err("Failed to get svc/pipe map: %d\n", ret);
+               return ret;
+       }
+
+       if (svc_to_pipe_map == 0) {
+               ret = -EIO;
+               ath10k_err("Invalid svc_to_pipe map\n");
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map,
+                                target_service_to_ce_map_wlan,
+                                sizeof(target_service_to_ce_map_wlan));
+       if (ret != 0) {
+               ath10k_err("Failed to write svc/pipe map: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+                                         offsetof(struct pcie_state,
+                                                  config_flags),
+                                         &pcie_config_flags);
+       if (ret != 0) {
+               ath10k_err("Failed to get pcie config_flags: %d\n", ret);
+               return ret;
+       }
+
+       pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
+
+       ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr +
+                                offsetof(struct pcie_state, config_flags),
+                                &pcie_config_flags,
+                                sizeof(pcie_config_flags));
+       if (ret != 0) {
+               ath10k_err("Failed to write pcie config_flags: %d\n", ret);
+               return ret;
+       }
+
+       /* configure early allocation */
+       ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc));
+
+       ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value);
+       if (ret != 0) {
+               ath10k_err("Faile to get early alloc val: %d\n", ret);
+               return ret;
+       }
+
+       /* first bank is switched to IRAM */
+       ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
+                        HI_EARLY_ALLOC_MAGIC_MASK);
+       ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
+                        HI_EARLY_ALLOC_IRAM_BANKS_MASK);
+
+       ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value);
+       if (ret != 0) {
+               ath10k_err("Failed to set early alloc val: %d\n", ret);
+               return ret;
+       }
+
+       /* Tell Target to proceed with initialization */
+       flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2));
+
+       ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value);
+       if (ret != 0) {
+               ath10k_err("Failed to get option val: %d\n", ret);
+               return ret;
+       }
+
+       flag2_value |= HI_OPTION_EARLY_CFG_DONE;
+
+       ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value);
+       if (ret != 0) {
+               ath10k_err("Failed to set option val: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+
+static int ath10k_pci_ce_init(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct hif_ce_pipe_info *pipe_info;
+       const struct ce_attr *attr;
+       int pipe_num;
+
+       for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+               pipe_info = &ar_pci->pipe_info[pipe_num];
+               pipe_info->pipe_num = pipe_num;
+               pipe_info->hif_ce_state = ar;
+               attr = &host_ce_config_wlan[pipe_num];
+
+               pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr);
+               if (pipe_info->ce_hdl == NULL) {
+                       ath10k_err("Unable to initialize CE for pipe: %d\n",
+                                  pipe_num);
+
+                       /* It is safe to call it here. It checks if ce_hdl is
+                        * valid for each pipe */
+                       ath10k_pci_ce_deinit(ar);
+                       return -1;
+               }
+
+               if (pipe_num == ar_pci->ce_count - 1) {
+                       /*
+                        * Reserve the ultimate CE for
+                        * diagnostic Window support
+                        */
+                       ar_pci->ce_diag =
+                       ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl;
+                       continue;
+               }
+
+               pipe_info->buf_sz = (size_t) (attr->src_sz_max);
+       }
+
+       /*
+        * Initially, establish CE completion handlers for use with BMI.
+        * These are overwritten with generic handlers after we exit BMI phase.
+        */
+       pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
+       ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+                                  ath10k_pci_bmi_send_done, 0);
+
+       pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
+       ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+                                  ath10k_pci_bmi_recv_data);
+
+       return 0;
+}
+
+static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       u32 fw_indicator_address, fw_indicator;
+
+       ath10k_pci_wake(ar);
+
+       fw_indicator_address = ar_pci->fw_indicator_address;
+       fw_indicator = ath10k_pci_read32(ar, fw_indicator_address);
+
+       if (fw_indicator & FW_IND_EVENT_PENDING) {
+               /* ACK: clear Target-side pending event */
+               ath10k_pci_write32(ar, fw_indicator_address,
+                                  fw_indicator & ~FW_IND_EVENT_PENDING);
+
+               if (ar_pci->started) {
+                       ath10k_pci_hif_dump_area(ar);
+               } else {
+                       /*
+                        * Probable Target failure before we're prepared
+                        * to handle it.  Generally unexpected.
+                        */
+                       ath10k_warn("early firmware event indicated\n");
+               }
+       }
+
+       ath10k_pci_sleep(ar);
+}
+
+static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
+       .send_head              = ath10k_pci_hif_send_head,
+       .exchange_bmi_msg       = ath10k_pci_hif_exchange_bmi_msg,
+       .start                  = ath10k_pci_hif_start,
+       .stop                   = ath10k_pci_hif_stop,
+       .map_service_to_pipe    = ath10k_pci_hif_map_service_to_pipe,
+       .get_default_pipe       = ath10k_pci_hif_get_default_pipe,
+       .send_complete_check    = ath10k_pci_hif_send_complete_check,
+       .init                   = ath10k_pci_hif_post_init,
+       .get_free_queue_number  = ath10k_pci_hif_get_free_queue_number,
+};
+
+static void ath10k_pci_ce_tasklet(unsigned long ptr)
+{
+       struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr;
+       struct ath10k_pci *ar_pci = pipe->ar_pci;
+
+       ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num);
+}
+
+static void ath10k_msi_err_tasklet(unsigned long data)
+{
+       struct ath10k *ar = (struct ath10k *)data;
+
+       ath10k_pci_fw_interrupt_handler(ar);
+}
+
+/*
+ * Handler for a per-engine interrupt on a PARTICULAR CE.
+ * This is used in cases where each CE has a private MSI interrupt.
+ */
+static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL;
+
+       if (ce_id < 0 || ce_id >= ARRAY_SIZE(ar_pci->pipe_info)) {
+               ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id);
+               return IRQ_HANDLED;
+       }
+
+       /*
+        * NOTE: We are able to derive ce_id from irq because we
+        * use a one-to-one mapping for CE's 0..5.
+        * CE's 6 & 7 do not use interrupts at all.
+        *
+        * This mapping must be kept in sync with the mapping
+        * used by firmware.
+        */
+       tasklet_schedule(&ar_pci->pipe_info[ce_id].intr);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       tasklet_schedule(&ar_pci->msi_fw_err);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Top-level interrupt handler for all PCI interrupts from a Target.
+ * When a block of MSI interrupts is allocated, this top-level handler
+ * is not used; instead, we directly call the correct sub-handler.
+ */
+static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
+{
+       struct ath10k *ar = arg;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       if (ar_pci->num_msi_intrs == 0) {
+               /*
+                * IMPORTANT: INTR_CLR regiser has to be set after
+                * INTR_ENABLE is set to 0, otherwise interrupt can not be
+                * really cleared.
+                */
+               iowrite32(0, ar_pci->mem +
+                         (SOC_CORE_BASE_ADDRESS |
+                          PCIE_INTR_ENABLE_ADDRESS));
+               iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                         PCIE_INTR_CE_MASK_ALL,
+                         ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                        PCIE_INTR_CLR_ADDRESS));
+               /*
+                * IMPORTANT: this extra read transaction is required to
+                * flush the posted write buffer.
+                */
+               (void) ioread32(ar_pci->mem +
+                               (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       }
+
+       tasklet_schedule(&ar_pci->intr_tq);
+
+       return IRQ_HANDLED;
+}
+
+static void ath10k_pci_tasklet(unsigned long data)
+{
+       struct ath10k *ar = (struct ath10k *)data;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
+       ath10k_ce_per_engine_service_any(ar);
+
+       if (ar_pci->num_msi_intrs == 0) {
+               /* Enable Legacy PCI line interrupts */
+               iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                         PCIE_INTR_CE_MASK_ALL,
+                         ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                        PCIE_INTR_ENABLE_ADDRESS));
+               /*
+                * IMPORTANT: this extra read transaction is required to
+                * flush the posted write buffer
+                */
+               (void) ioread32(ar_pci->mem +
+                               (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       }
+}
+
+static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+       int i;
+
+       ret = pci_enable_msi_block(ar_pci->pdev, num);
+       if (ret)
+               return ret;
+
+       ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
+                         ath10k_pci_msi_fw_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret)
+               return ret;
+
+       for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
+               ret = request_irq(ar_pci->pdev->irq + i,
+                                 ath10k_pci_per_engine_handler,
+                                 IRQF_SHARED, "ath10k_pci", ar);
+               if (ret) {
+                       ath10k_warn("request_irq(%d) failed %d\n",
+                                   ar_pci->pdev->irq + i, ret);
+
+                       for (; i >= MSI_ASSIGN_CE_INITIAL; i--)
+                               free_irq(ar_pci->pdev->irq, ar);
+
+                       pci_disable_msi(ar_pci->pdev);
+                       return ret;
+               }
+       }
+
+       ath10k_info("MSI-X interrupt handling (%d intrs)\n", num);
+       return 0;
+}
+
+static int ath10k_pci_start_intr_msi(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = pci_enable_msi(ar_pci->pdev);
+       if (ret < 0)
+               return ret;
+
+       ret = request_irq(ar_pci->pdev->irq,
+                         ath10k_pci_interrupt_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret < 0) {
+               pci_disable_msi(ar_pci->pdev);
+               return ret;
+       }
+
+       ath10k_info("MSI interrupt handling\n");
+       return 0;
+}
+
+static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ret = request_irq(ar_pci->pdev->irq,
+                         ath10k_pci_interrupt_handler,
+                         IRQF_SHARED, "ath10k_pci", ar);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Make sure to wake the Target before enabling Legacy
+        * Interrupt.
+        */
+       iowrite32(PCIE_SOC_WAKE_V_MASK,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_pci_wait(ar);
+
+       /*
+        * A potential race occurs here: The CORE_BASE write
+        * depends on target correctly decoding AXI address but
+        * host won't know when target writes BAR to CORE_CTRL.
+        * This write might get lost if target has NOT written BAR.
+        * For now, fix the race by repeating the write in below
+        * synchronization checking.
+        */
+       iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                 PCIE_INTR_CE_MASK_ALL,
+                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                PCIE_INTR_ENABLE_ADDRESS));
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_info("legacy interrupt handling\n");
+       return 0;
+}
+
+static int ath10k_pci_start_intr(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int num = MSI_NUM_REQUEST;
+       int ret;
+       int i;
+
+       tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar);
+       tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
+                    (unsigned long) ar);
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ar_pci->pipe_info[i].ar_pci = ar_pci;
+               tasklet_init(&ar_pci->pipe_info[i].intr,
+                            ath10k_pci_ce_tasklet,
+                            (unsigned long)&ar_pci->pipe_info[i]);
+       }
+
+       if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features))
+               num = 1;
+
+       if (num > 1) {
+               ret = ath10k_pci_start_intr_msix(ar, num);
+               if (ret == 0)
+                       goto exit;
+
+               ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret);
+               num = 1;
+       }
+
+       if (num == 1) {
+               ret = ath10k_pci_start_intr_msi(ar);
+               if (ret == 0)
+                       goto exit;
+
+               ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n",
+                           ret);
+               num = 0;
+       }
+
+       ret = ath10k_pci_start_intr_legacy(ar);
+
+exit:
+       ar_pci->num_msi_intrs = num;
+       ar_pci->ce_count = CE_COUNT;
+       return ret;
+}
+
+static void ath10k_pci_stop_intr(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int i;
+
+       /* There's at least one interrupt irregardless whether its legacy INTR
+        * or MSI or MSI-X */
+       for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+               free_irq(ar_pci->pdev->irq + i, ar);
+
+       if (ar_pci->num_msi_intrs > 0)
+               pci_disable_msi(ar_pci->pdev);
+}
+
+static int ath10k_pci_reset_target(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int wait_limit = 300; /* 3 sec */
+
+       /* Wait for Target to finish initialization before we proceed. */
+       iowrite32(PCIE_SOC_WAKE_V_MASK,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       ath10k_pci_wait(ar);
+
+       while (wait_limit-- &&
+              !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
+                FW_IND_INITIALIZED)) {
+               if (ar_pci->num_msi_intrs == 0)
+                       /* Fix potential race by repeating CORE_BASE writes */
+                       iowrite32(PCIE_INTR_FIRMWARE_MASK |
+                                 PCIE_INTR_CE_MASK_ALL,
+                                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+                                                PCIE_INTR_ENABLE_ADDRESS));
+               mdelay(10);
+       }
+
+       if (wait_limit < 0) {
+               ath10k_err("Target stalled\n");
+               iowrite32(PCIE_SOC_WAKE_RESET,
+                         ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                         PCIE_SOC_WAKE_ADDRESS);
+               return -EIO;
+       }
+
+       iowrite32(PCIE_SOC_WAKE_RESET,
+                 ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+                 PCIE_SOC_WAKE_ADDRESS);
+
+       return 0;
+}
+
+static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+{
+       struct ath10k *ar = ar_pci->ar;
+       void __iomem *mem = ar_pci->mem;
+       int i;
+       u32 val;
+
+       if (!SOC_GLOBAL_RESET_ADDRESS)
+               return;
+
+       if (!mem)
+               return;
+
+       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS,
+                              PCIE_SOC_WAKE_V_MASK);
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (ath10k_pci_target_is_awake(ar))
+                       break;
+               msleep(1);
+       }
+
+       /* Put Target, including PCIe, into RESET. */
+       val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS);
+       val |= 1;
+       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+                                         RTC_STATE_COLD_RESET_MASK)
+                       break;
+               msleep(1);
+       }
+
+       /* Pull Target, including PCIe, out of RESET. */
+       val &= ~1;
+       ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+       for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+               if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+                                           RTC_STATE_COLD_RESET_MASK))
+                       break;
+               msleep(1);
+       }
+
+       ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
+}
+
+static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
+{
+       int i;
+
+       for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) {
+               if (!test_bit(i, ar_pci->features))
+                       continue;
+
+               switch (i) {
+               case ATH10K_PCI_FEATURE_MSI_X:
+                       ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
+                       break;
+               case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
+                       ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
+                       break;
+               }
+       }
+}
+
+static int ath10k_pci_probe(struct pci_dev *pdev,
+                           const struct pci_device_id *pci_dev)
+{
+       void __iomem *mem;
+       int ret = 0;
+       struct ath10k *ar;
+       struct ath10k_pci *ar_pci;
+       u32 lcr_val;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
+       if (ar_pci == NULL)
+               return -ENOMEM;
+
+       ar_pci->pdev = pdev;
+       ar_pci->dev = &pdev->dev;
+
+       switch (pci_dev->device) {
+       case QCA988X_1_0_DEVICE_ID:
+               set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+               break;
+       case QCA988X_2_0_DEVICE_ID:
+               set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
+               break;
+       default:
+               ret = -ENODEV;
+               ath10k_err("Unkown device ID: %d\n", pci_dev->device);
+               goto err_ar_pci;
+       }
+
+       ath10k_pci_dump_features(ar_pci);
+
+       ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
+                               &ath10k_pci_hif_ops);
+       if (!ar) {
+               ath10k_err("ath10k_core_create failed!\n");
+               ret = -EINVAL;
+               goto err_ar_pci;
+       }
+
+       /* Enable QCA988X_1.0 HW workarounds */
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+               spin_lock_init(&ar_pci->hw_v1_workaround_lock);
+
+       ar_pci->ar = ar;
+       ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
+       atomic_set(&ar_pci->keep_awake_count, 0);
+
+       pci_set_drvdata(pdev, ar);
+
+       /*
+        * Without any knowledge of the Host, the Target may have been reset or
+        * power cycled and its Config Space may no longer reflect the PCI
+        * address space that was assigned earlier by the PCI infrastructure.
+        * Refresh it now.
+        */
+       ret = pci_assign_resource(pdev, BAR_NUM);
+       if (ret) {
+               ath10k_err("cannot assign PCI space: %d\n", ret);
+               goto err_ar;
+       }
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               ath10k_err("cannot enable PCI device: %d\n", ret);
+               goto err_ar;
+       }
+
+       /* Request MMIO resources */
+       ret = pci_request_region(pdev, BAR_NUM, "ath");
+       if (ret) {
+               ath10k_err("PCI MMIO reservation error: %d\n", ret);
+               goto err_device;
+       }
+
+       /*
+        * Target structures have a limit of 32 bit DMA pointers.
+        * DMA pointers can be wider than 32 bits by default on some systems.
+        */
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               ath10k_err("32-bit DMA not available: %d\n", ret);
+               goto err_region;
+       }
+
+       ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret) {
+               ath10k_err("cannot enable 32-bit consistent DMA\n");
+               goto err_region;
+       }
+
+       /* Set bus master bit in PCI_COMMAND to enable DMA */
+       pci_set_master(pdev);
+
+       /*
+        * Temporary FIX: disable ASPM
+        * Will be removed after the OTP is programmed
+        */
+       pci_read_config_dword(pdev, 0x80, &lcr_val);
+       pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00));
+
+       /* Arrange for access to Target SoC registers. */
+       mem = pci_iomap(pdev, BAR_NUM, 0);
+       if (!mem) {
+               ath10k_err("PCI iomap error\n");
+               ret = -EIO;
+               goto err_master;
+       }
+
+       ar_pci->mem = mem;
+
+       spin_lock_init(&ar_pci->ce_lock);
+
+       ar_pci->cacheline_sz = dma_get_cache_alignment();
+
+       ret = ath10k_pci_start_intr(ar);
+       if (ret) {
+               ath10k_err("could not start interrupt handling (%d)\n", ret);
+               goto err_iomap;
+       }
+
+       /*
+        * Bring the target up cleanly.
+        *
+        * The target may be in an undefined state with an AUX-powered Target
+        * and a Host in WoW mode. If the Host crashes, loses power, or is
+        * restarted (without unloading the driver) then the Target is left
+        * (aux) powered and running. On a subsequent driver load, the Target
+        * is in an unexpected state. We try to catch that here in order to
+        * reset the Target and retry the probe.
+        */
+       ath10k_pci_device_reset(ar_pci);
+
+       ret = ath10k_pci_reset_target(ar);
+       if (ret)
+               goto err_intr;
+
+       if (ath10k_target_ps) {
+               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+       } else {
+               /* Force AWAKE forever */
+               ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+               ath10k_do_pci_wake(ar);
+       }
+
+       ret = ath10k_pci_ce_init(ar);
+       if (ret)
+               goto err_intr;
+
+       ret = ath10k_pci_init_config(ar);
+       if (ret)
+               goto err_ce;
+
+       ret = ath10k_pci_wake_target_cpu(ar);
+       if (ret) {
+               ath10k_err("could not wake up target CPU (%d)\n", ret);
+               goto err_ce;
+       }
+
+       ret = ath10k_core_register(ar);
+       if (ret) {
+               ath10k_err("could not register driver core (%d)\n", ret);
+               goto err_ce;
+       }
+
+       return 0;
+
+err_ce:
+       ath10k_pci_ce_deinit(ar);
+err_intr:
+       ath10k_pci_stop_intr(ar);
+err_iomap:
+       pci_iounmap(pdev, mem);
+err_master:
+       pci_clear_master(pdev);
+err_region:
+       pci_release_region(pdev, BAR_NUM);
+err_device:
+       pci_disable_device(pdev);
+err_ar:
+       pci_set_drvdata(pdev, NULL);
+       ath10k_core_destroy(ar);
+err_ar_pci:
+       /* call HIF PCI free here */
+       kfree(ar_pci);
+
+       return ret;
+}
+
+static void ath10k_pci_remove(struct pci_dev *pdev)
+{
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return;
+
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci)
+               return;
+
+       tasklet_kill(&ar_pci->msi_fw_err);
+
+       ath10k_core_unregister(ar);
+       ath10k_pci_stop_intr(ar);
+
+       pci_set_drvdata(pdev, NULL);
+       pci_iounmap(pdev, ar_pci->mem);
+       pci_release_region(pdev, BAR_NUM);
+       pci_clear_master(pdev);
+       pci_disable_device(pdev);
+
+       ath10k_core_destroy(ar);
+       kfree(ar_pci);
+}
+
+#if defined(CONFIG_PM_SLEEP)
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_suspend(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+       u32 val;
+       int ret, retval;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return -ENODEV;
+
+       ar_pci = ath10k_pci_priv(ar);
+       if (!ar_pci)
+               return -ENODEV;
+
+       if (ath10k_core_target_suspend(ar))
+               return -EBUSY;
+
+       ret = wait_event_interruptible_timeout(ar->event_queue,
+                                               ar->is_target_paused == true,
+                                               1 * HZ);
+       if (ret < 0) {
+               ath10k_warn("suspend interrupted (%d)\n", ret);
+               retval = ret;
+               goto resume;
+       } else if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               retval = EIO;
+               goto resume;
+       }
+
+       /*
+        * reset is_target_paused and host can check that in next time,
+        * or it will always be TRUE and host just skip the waiting
+        * condition, it causes target assert due to host already
+        * suspend
+        */
+       ar->is_target_paused = false;
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0x3) {
+               pci_save_state(pdev);
+               pci_disable_device(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      (val & 0xffffff00) | 0x03);
+       }
+
+       return 0;
+resume:
+       ret = ath10k_core_target_resume(ar);
+       if (ret)
+               ath10k_warn("could not resume (%d)\n", ret);
+
+       return retval;
+}
+
+static int ath10k_pci_resume(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct ath10k *ar = pci_get_drvdata(pdev);
+       struct ath10k_pci *ar_pci;
+       int ret;
+       u32 val;
+
+       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+       if (!ar)
+               return -ENODEV;
+       ar_pci = ath10k_pci_priv(ar);
+
+       if (!ar_pci)
+               return -ENODEV;
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               ath10k_warn("cannot enable PCI device: %d\n", ret);
+               return ret;
+       }
+
+       pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+       if ((val & 0x000000ff) != 0) {
+               pci_restore_state(pdev);
+               pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+                                      val & 0xffffff00);
+               /*
+                * Suspend/Resume resets the PCI configuration space,
+                * so we have to re-disable the RETRY_TIMEOUT register (0x41)
+                * to keep PCI Tx retries from interfering with C3 CPU state
+                */
+               pci_read_config_dword(pdev, 0x40, &val);
+
+               if ((val & 0x0000ff00) != 0)
+                       pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+       }
+
+       ret = ath10k_core_target_resume(ar);
+       if (ret)
+               ath10k_warn("target resume failed: %d\n", ret);
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
+                        ath10k_pci_suspend,
+                        ath10k_pci_resume);
+
+#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
+
+#else
+
+#define ATH10K_PCI_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
+MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
+
+static struct pci_driver ath10k_pci_driver = {
+       .name = "ath10k_pci",
+       .id_table = ath10k_pci_id_table,
+       .probe = ath10k_pci_probe,
+       .remove = ath10k_pci_remove,
+       .driver.pm = ATH10K_PCI_PM_OPS,
+};
+
+static int __init ath10k_pci_init(void)
+{
+       int ret;
+
+       ret = pci_register_driver(&ath10k_pci_driver);
+       if (ret)
+               ath10k_err("pci_register_driver failed [%d]\n", ret);
+
+       return ret;
+}
+module_init(ath10k_pci_init);
+
+static void __exit ath10k_pci_exit(void)
+{
+       pci_unregister_driver(&ath10k_pci_driver);
+}
+
+module_exit(ath10k_pci_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
new file mode 100644 (file)
index 0000000..d2a055a
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _PCI_H_
+#define _PCI_H_
+
+#include <linux/interrupt.h>
+
+#include "hw.h"
+#include "ce.h"
+
+/* FW dump area */
+#define REG_DUMP_COUNT_QCA988X 60
+
+/*
+ * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+/*
+ * maximum number of bytes that can be
+ * handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+struct bmi_xfer {
+       struct completion done;
+       bool wait_for_resp;
+       u32 resp_len;
+};
+
+struct ath10k_pci_compl {
+       struct list_head list;
+       int send_or_recv;
+       struct ce_state *ce_state;
+       struct hif_ce_pipe_info *pipe_info;
+       void *transfer_context;
+       unsigned int nbytes;
+       unsigned int transfer_id;
+       unsigned int flags;
+};
+
+/* compl_state.send_or_recv */
+#define HIF_CE_COMPLETE_FREE 0
+#define HIF_CE_COMPLETE_SEND 1
+#define HIF_CE_COMPLETE_RECV 2
+
+/*
+ * PCI-specific Target state
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ *
+ * Much of this may be of interest to the Host so
+ * HOST_INTEREST->hi_interconnect_state points here
+ * (and all members are 32-bit quantities in order to
+ * facilitate Host access). In particular, Host software is
+ * required to initialize pipe_cfg_addr and svc_to_pipe_map.
+ */
+struct pcie_state {
+       /* Pipe configuration Target address */
+       /* NB: ce_pipe_config[CE_COUNT] */
+       u32 pipe_cfg_addr;
+
+       /* Service to pipe map Target address */
+       /* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */
+       u32 svc_to_pipe_map;
+
+       /* number of MSI interrupts requested */
+       u32 msi_requested;
+
+       /* number of MSI interrupts granted */
+       u32 msi_granted;
+
+       /* Message Signalled Interrupt address */
+       u32 msi_addr;
+
+       /* Base data */
+       u32 msi_data;
+
+       /*
+        * Data for firmware interrupt;
+        * MSI data for other interrupts are
+        * in various SoC registers
+        */
+       u32 msi_fw_intr_data;
+
+       /* PCIE_PWR_METHOD_* */
+       u32 power_mgmt_method;
+
+       /* PCIE_CONFIG_FLAG_* */
+       u32 config_flags;
+};
+
+/* PCIE_CONFIG_FLAG definitions */
+#define PCIE_CONFIG_FLAG_ENABLE_L1  0x0000001
+
+/* Host software's Copy Engine configuration. */
+#define CE_ATTR_FLAGS 0
+
+/*
+ * Configuration information for a Copy Engine pipe.
+ * Passed from Host to Target during startup (one per CE).
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ */
+struct ce_pipe_config {
+       u32 pipenum;
+       u32 pipedir;
+       u32 nentries;
+       u32 nbytes_max;
+       u32 flags;
+       u32 reserved;
+};
+
+/*
+ * Directions for interconnect pipe configuration.
+ * These definitions may be used during configuration and are shared
+ * between Host and Target.
+ *
+ * Pipe Directions are relative to the Host, so PIPEDIR_IN means
+ * "coming IN over air through Target to Host" as with a WiFi Rx operation.
+ * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air"
+ * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man"
+ * Target since things that are "PIPEDIR_OUT" are coming IN to the Target
+ * over the interconnect.
+ */
+#define PIPEDIR_NONE    0
+#define PIPEDIR_IN      1  /* Target-->Host, WiFi Rx direction */
+#define PIPEDIR_OUT     2  /* Host->Target, WiFi Tx direction */
+#define PIPEDIR_INOUT   3  /* bidirectional */
+
+/* Establish a mapping between a service/direction and a pipe. */
+struct service_to_pipe {
+       u32 service_id;
+       u32 pipedir;
+       u32 pipenum;
+};
+
+enum ath10k_pci_features {
+       ATH10K_PCI_FEATURE_MSI_X                = 0,
+       ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND    = 1,
+
+       /* keep last */
+       ATH10K_PCI_FEATURE_COUNT
+};
+
+/* Per-pipe state. */
+struct hif_ce_pipe_info {
+       /* Handle of underlying Copy Engine */
+       struct ce_state *ce_hdl;
+
+       /* Our pipe number; facilitiates use of pipe_info ptrs. */
+       u8 pipe_num;
+
+       /* Convenience back pointer to hif_ce_state. */
+       struct ath10k *hif_ce_state;
+
+       size_t buf_sz;
+
+       /* protects compl_free and num_send_allowed */
+       spinlock_t pipe_lock;
+
+       /* List of free CE completion slots */
+       struct list_head compl_free;
+
+       /* Limit the number of outstanding send requests. */
+       int num_sends_allowed;
+
+       struct ath10k_pci *ar_pci;
+       struct tasklet_struct intr;
+};
+
+struct ath10k_pci {
+       struct pci_dev *pdev;
+       struct device *dev;
+       struct ath10k *ar;
+       void __iomem *mem;
+       int cacheline_sz;
+
+       DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
+
+       /*
+        * Number of MSI interrupts granted, 0 --> using legacy PCI line
+        * interrupts.
+        */
+       int num_msi_intrs;
+
+       struct tasklet_struct intr_tq;
+       struct tasklet_struct msi_fw_err;
+
+       /* Number of Copy Engines supported */
+       unsigned int ce_count;
+
+       int started;
+
+       atomic_t keep_awake_count;
+       bool verified_awake;
+
+       /* List of CE completions to be processed */
+       struct list_head compl_process;
+
+       /* protects compl_processing and compl_process */
+       spinlock_t compl_lock;
+
+       bool compl_processing;
+
+       struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX];
+
+       struct ath10k_hif_cb msg_callbacks_current;
+
+       /* Target address used to signal a pending firmware event */
+       u32 fw_indicator_address;
+
+       /* Copy Engine used for Diagnostic Accesses */
+       struct ce_state *ce_diag;
+
+       /* FIXME: document what this really protects */
+       spinlock_t ce_lock;
+
+       /* Map CE id to ce_state */
+       struct ce_state *ce_id_to_state[CE_COUNT_MAX];
+
+       /* makes sure that dummy reads are atomic */
+       spinlock_t hw_v1_workaround_lock;
+};
+
+static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
+{
+       return ar->hif.priv;
+}
+
+static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr)
+{
+       return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val)
+{
+       iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
+#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */
+
+#define BAR_NUM 0
+
+#define CDC_WAR_MAGIC_STR   0xceef0000
+#define CDC_WAR_DATA_CE     4
+
+/*
+ * TODO: Should be a function call specific to each Target-type.
+ * This convoluted macro converts from Target CPU Virtual Address Space to CE
+ * Address Space. As part of this process, we conservatively fetch the current
+ * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space
+ * for this device; but that's not guaranteed.
+ */
+#define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr)                 \
+       (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS|                  \
+         CORE_CTRL_ADDRESS)) & 0x7ff) << 21) |                         \
+        0x100000 | ((addr) & 0xfffff))
+
+/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
+#define DIAG_ACCESS_CE_TIMEOUT_MS 10
+
+/*
+ * This API allows the Host to access Target registers directly
+ * and relatively efficiently over PCIe.
+ * This allows the Host to avoid extra overhead associated with
+ * sending a message to firmware and waiting for a response message
+ * from firmware, as is done on other interconnects.
+ *
+ * Yet there is some complexity with direct accesses because the
+ * Target's power state is not known a priori. The Host must issue
+ * special PCIe reads/writes in order to explicitly wake the Target
+ * and to verify that it is awake and will remain awake.
+ *
+ * Usage:
+ *
+ *   Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space.
+ *   These calls must be bracketed by ath10k_pci_wake and
+ *   ath10k_pci_sleep.  A single BEGIN/END pair is adequate for
+ *   multiple READ/WRITE operations.
+ *
+ *   Use ath10k_pci_wake to put the Target in a state in
+ *   which it is legal for the Host to directly access it. This
+ *   may involve waking the Target from a low power state, which
+ *   may take up to 2Ms!
+ *
+ *   Use ath10k_pci_sleep to tell the Target that as far as
+ *   this code path is concerned, it no longer needs to remain
+ *   directly accessible.  BEGIN/END is under a reference counter;
+ *   multiple code paths may issue BEGIN/END on a single targid.
+ */
+static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
+                                     u32 value)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       void __iomem *addr = ar_pci->mem;
+
+       if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+               unsigned long irq_flags;
+
+               spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
+
+               ioread32(addr+offset+4); /* 3rd read prior to write */
+               ioread32(addr+offset+4); /* 2nd read prior to write */
+               ioread32(addr+offset+4); /* 1st read prior to write */
+               iowrite32(value, addr+offset);
+
+               spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock,
+                                      irq_flags);
+       } else {
+               iowrite32(value, addr+offset);
+       }
+}
+
+static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       return ioread32(ar_pci->mem + offset);
+}
+
+extern unsigned int ath10k_target_ps;
+
+void ath10k_do_pci_wake(struct ath10k *ar);
+void ath10k_do_pci_sleep(struct ath10k *ar);
+
+static inline void ath10k_pci_wake(struct ath10k *ar)
+{
+       if (ath10k_target_ps)
+               ath10k_do_pci_wake(ar);
+}
+
+static inline void ath10k_pci_sleep(struct ath10k *ar)
+{
+       if (ath10k_target_ps)
+               ath10k_do_pci_sleep(ar);
+}
+
+#endif /* _PCI_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
new file mode 100644 (file)
index 0000000..bfec6c8
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _RX_DESC_H_
+#define _RX_DESC_H_
+
+enum rx_attention_flags {
+       RX_ATTENTION_FLAGS_FIRST_MPDU          = 1 << 0,
+       RX_ATTENTION_FLAGS_LAST_MPDU           = 1 << 1,
+       RX_ATTENTION_FLAGS_MCAST_BCAST         = 1 << 2,
+       RX_ATTENTION_FLAGS_PEER_IDX_INVALID    = 1 << 3,
+       RX_ATTENTION_FLAGS_PEER_IDX_TIMEOUT    = 1 << 4,
+       RX_ATTENTION_FLAGS_POWER_MGMT          = 1 << 5,
+       RX_ATTENTION_FLAGS_NON_QOS             = 1 << 6,
+       RX_ATTENTION_FLAGS_NULL_DATA           = 1 << 7,
+       RX_ATTENTION_FLAGS_MGMT_TYPE           = 1 << 8,
+       RX_ATTENTION_FLAGS_CTRL_TYPE           = 1 << 9,
+       RX_ATTENTION_FLAGS_MORE_DATA           = 1 << 10,
+       RX_ATTENTION_FLAGS_EOSP                = 1 << 11,
+       RX_ATTENTION_FLAGS_U_APSD_TRIGGER      = 1 << 12,
+       RX_ATTENTION_FLAGS_FRAGMENT            = 1 << 13,
+       RX_ATTENTION_FLAGS_ORDER               = 1 << 14,
+       RX_ATTENTION_FLAGS_CLASSIFICATION      = 1 << 15,
+       RX_ATTENTION_FLAGS_OVERFLOW_ERR        = 1 << 16,
+       RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR     = 1 << 17,
+       RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL = 1 << 18,
+       RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL      = 1 << 19,
+       RX_ATTENTION_FLAGS_SA_IDX_INVALID      = 1 << 20,
+       RX_ATTENTION_FLAGS_DA_IDX_INVALID      = 1 << 21,
+       RX_ATTENTION_FLAGS_SA_IDX_TIMEOUT      = 1 << 22,
+       RX_ATTENTION_FLAGS_DA_IDX_TIMEOUT      = 1 << 23,
+       RX_ATTENTION_FLAGS_ENCRYPT_REQUIRED    = 1 << 24,
+       RX_ATTENTION_FLAGS_DIRECTED            = 1 << 25,
+       RX_ATTENTION_FLAGS_BUFFER_FRAGMENT     = 1 << 26,
+       RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR     = 1 << 27,
+       RX_ATTENTION_FLAGS_TKIP_MIC_ERR        = 1 << 28,
+       RX_ATTENTION_FLAGS_DECRYPT_ERR         = 1 << 29,
+       RX_ATTENTION_FLAGS_FCS_ERR             = 1 << 30,
+       RX_ATTENTION_FLAGS_MSDU_DONE           = 1 << 31,
+};
+
+struct rx_attention {
+       __le32 flags; /* %RX_ATTENTION_FLAGS_ */
+} __packed;
+
+/*
+ * first_mpdu
+ *             Indicates the first MSDU of the PPDU.  If both first_mpdu
+ *             and last_mpdu are set in the MSDU then this is a not an
+ *             A-MPDU frame but a stand alone MPDU.  Interior MPDU in an
+ *             A-MPDU shall have both first_mpdu and last_mpdu bits set to
+ *             0.  The PPDU start status will only be valid when this bit
+ *             is set.
+ *
+ * last_mpdu
+ *             Indicates the last MSDU of the last MPDU of the PPDU.  The
+ *             PPDU end status will only be valid when this bit is set.
+ *
+ * mcast_bcast
+ *             Multicast / broadcast indicator.  Only set when the MAC
+ *             address 1 bit 0 is set indicating mcast/bcast and the BSSID
+ *             matches one of the 4 BSSID registers. Only set when
+ *             first_msdu is set.
+ *
+ * peer_idx_invalid
+ *             Indicates no matching entries within the the max search
+ *             count.  Only set when first_msdu is set.
+ *
+ * peer_idx_timeout
+ *             Indicates an unsuccessful search for the peer index due to
+ *             timeout.  Only set when first_msdu is set.
+ *
+ * power_mgmt
+ *             Power management bit set in the 802.11 header.  Only set
+ *             when first_msdu is set.
+ *
+ * non_qos
+ *             Set if packet is not a non-QoS data frame.  Only set when
+ *             first_msdu is set.
+ *
+ * null_data
+ *             Set if frame type indicates either null data or QoS null
+ *             data format.  Only set when first_msdu is set.
+ *
+ * mgmt_type
+ *             Set if packet is a management packet.  Only set when
+ *             first_msdu is set.
+ *
+ * ctrl_type
+ *             Set if packet is a control packet.  Only set when first_msdu
+ *             is set.
+ *
+ * more_data
+ *             Set if more bit in frame control is set.  Only set when
+ *             first_msdu is set.
+ *
+ * eosp
+ *             Set if the EOSP (end of service period) bit in the QoS
+ *             control field is set.  Only set when first_msdu is set.
+ *
+ * u_apsd_trigger
+ *             Set if packet is U-APSD trigger.  Key table will have bits
+ *             per TID to indicate U-APSD trigger.
+ *
+ * fragment
+ *             Indicates that this is an 802.11 fragment frame.  This is
+ *             set when either the more_frag bit is set in the frame
+ *             control or the fragment number is not zero.  Only set when
+ *             first_msdu is set.
+ *
+ * order
+ *             Set if the order bit in the frame control is set.  Only set
+ *             when first_msdu is set.
+ *
+ * classification
+ *             Indicates that this status has a corresponding MSDU that
+ *             requires FW processing.  The OLE will have classification
+ *             ring mask registers which will indicate the ring(s) for
+ *             packets and descriptors which need FW attention.
+ *
+ * overflow_err
+ *             PCU Receive FIFO does not have enough space to store the
+ *             full receive packet.  Enough space is reserved in the
+ *             receive FIFO for the status is written.  This MPDU remaining
+ *             packets in the PPDU will be filtered and no Ack response
+ *             will be transmitted.
+ *
+ * msdu_length_err
+ *             Indicates that the MSDU length from the 802.3 encapsulated
+ *             length field extends beyond the MPDU boundary.
+ *
+ * tcp_udp_chksum_fail
+ *             Indicates that the computed checksum (tcp_udp_chksum) did
+ *             not match the checksum in the TCP/UDP header.
+ *
+ * ip_chksum_fail
+ *             Indicates that the computed checksum did not match the
+ *             checksum in the IP header.
+ *
+ * sa_idx_invalid
+ *             Indicates no matching entry was found in the address search
+ *             table for the source MAC address.
+ *
+ * da_idx_invalid
+ *             Indicates no matching entry was found in the address search
+ *             table for the destination MAC address.
+ *
+ * sa_idx_timeout
+ *             Indicates an unsuccessful search for the source MAC address
+ *             due to the expiring of the search timer.
+ *
+ * da_idx_timeout
+ *             Indicates an unsuccessful search for the destination MAC
+ *             address due to the expiring of the search timer.
+ *
+ * encrypt_required
+ *             Indicates that this data type frame is not encrypted even if
+ *             the policy for this MPDU requires encryption as indicated in
+ *             the peer table key type.
+ *
+ * directed
+ *             MPDU is a directed packet which means that the RA matched
+ *             our STA addresses.  In proxySTA it means that the TA matched
+ *             an entry in our address search table with the corresponding
+ *             'no_ack' bit is the address search entry cleared.
+ *
+ * buffer_fragment
+ *             Indicates that at least one of the rx buffers has been
+ *             fragmented.  If set the FW should look at the rx_frag_info
+ *             descriptor described below.
+ *
+ * mpdu_length_err
+ *             Indicates that the MPDU was pre-maturely terminated
+ *             resulting in a truncated MPDU.  Don't trust the MPDU length
+ *             field.
+ *
+ * tkip_mic_err
+ *             Indicates that the MPDU Michael integrity check failed
+ *
+ * decrypt_err
+ *             Indicates that the MPDU decrypt integrity check failed
+ *
+ * fcs_err
+ *             Indicates that the MPDU FCS check failed
+ *
+ * msdu_done
+ *             If set indicates that the RX packet data, RX header data, RX
+ *             PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU
+ *             start/end descriptors and RX Attention descriptor are all
+ *             valid.  This bit must be in the last octet of the
+ *             descriptor.
+ */
+
+struct rx_frag_info {
+       u8 ring0_more_count;
+       u8 ring1_more_count;
+       u8 ring2_more_count;
+       u8 ring3_more_count;
+} __packed;
+
+/*
+ * ring0_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 0.  Field is filled in by the RX_DMA.
+ *
+ * ring1_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 1. Field is filled in by the RX_DMA.
+ *
+ * ring2_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 2. Field is filled in by the RX_DMA.
+ *
+ * ring3_more_count
+ *             Indicates the number of more buffers associated with RX DMA
+ *             ring 3. Field is filled in by the RX_DMA.
+ */
+
+enum htt_rx_mpdu_encrypt_type {
+       HTT_RX_MPDU_ENCRYPT_WEP40            = 0,
+       HTT_RX_MPDU_ENCRYPT_WEP104           = 1,
+       HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC = 2,
+       HTT_RX_MPDU_ENCRYPT_WEP128           = 3,
+       HTT_RX_MPDU_ENCRYPT_TKIP_WPA         = 4,
+       HTT_RX_MPDU_ENCRYPT_WAPI             = 5,
+       HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2     = 6,
+       HTT_RX_MPDU_ENCRYPT_NONE             = 7,
+};
+
+#define RX_MPDU_START_INFO0_PEER_IDX_MASK     0x000007ff
+#define RX_MPDU_START_INFO0_PEER_IDX_LSB      0
+#define RX_MPDU_START_INFO0_SEQ_NUM_MASK      0x0fff0000
+#define RX_MPDU_START_INFO0_SEQ_NUM_LSB       16
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_MASK 0xf0000000
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_LSB  28
+#define RX_MPDU_START_INFO0_FROM_DS           (1 << 11)
+#define RX_MPDU_START_INFO0_TO_DS             (1 << 12)
+#define RX_MPDU_START_INFO0_ENCRYPTED         (1 << 13)
+#define RX_MPDU_START_INFO0_RETRY             (1 << 14)
+#define RX_MPDU_START_INFO0_TXBF_H_INFO       (1 << 15)
+
+#define RX_MPDU_START_INFO1_TID_MASK 0xf0000000
+#define RX_MPDU_START_INFO1_TID_LSB  28
+#define RX_MPDU_START_INFO1_DIRECTED (1 << 16)
+
+struct rx_mpdu_start {
+       __le32 info0;
+       union {
+               struct {
+                       __le32 pn31_0;
+                       __le32 info1; /* %RX_MPDU_START_INFO1_ */
+               } __packed;
+               struct {
+                       u8 pn[6];
+               } __packed;
+       } __packed;
+} __packed;
+
+/*
+ * peer_idx
+ *             The index of the address search table which associated with
+ *             the peer table entry corresponding to this MPDU.  Only valid
+ *             when first_msdu is set.
+ *
+ * fr_ds
+ *             Set if the from DS bit is set in the frame control.  Only
+ *             valid when first_msdu is set.
+ *
+ * to_ds
+ *             Set if the to DS bit is set in the frame control.  Only
+ *             valid when first_msdu is set.
+ *
+ * encrypted
+ *             Protected bit from the frame control.  Only valid when
+ *             first_msdu is set.
+ *
+ * retry
+ *             Retry bit from the frame control.  Only valid when
+ *             first_msdu is set.
+ *
+ * txbf_h_info
+ *             The MPDU data will contain H information.  Primarily used
+ *             for debug.
+ *
+ * seq_num
+ *             The sequence number from the 802.11 header.  Only valid when
+ *             first_msdu is set.
+ *
+ * encrypt_type
+ *             Indicates type of decrypt cipher used (as defined in the
+ *             peer table)
+ *             0: WEP40
+ *             1: WEP104
+ *             2: TKIP without MIC
+ *             3: WEP128
+ *             4: TKIP (WPA)
+ *             5: WAPI
+ *             6: AES-CCM (WPA2)
+ *             7: No cipher
+ *             Only valid when first_msdu_is set
+ *
+ * pn_31_0
+ *             Bits [31:0] of the PN number extracted from the IV field
+ *             WEP: IV = {key_id_octet, pn2, pn1, pn0}.  Only pn[23:0] is
+ *             valid.
+ *             TKIP: IV = {pn5, pn4, pn3, pn2, key_id_octet, pn0,
+ *             WEPSeed[1], pn1}.  Only pn[47:0] is valid.
+ *             AES-CCM: IV = {pn5, pn4, pn3, pn2, key_id_octet, 0x0, pn1,
+ *             pn0}.  Only pn[47:0] is valid.
+ *             WAPI: IV = {key_id_octet, 0x0, pn15, pn14, pn13, pn12, pn11,
+ *             pn10, pn9, pn8, pn7, pn6, pn5, pn4, pn3, pn2, pn1, pn0}.
+ *             The ext_wapi_pn[127:48] in the rx_msdu_misc descriptor and
+ *             pn[47:0] are valid.
+ *             Only valid when first_msdu is set.
+ *
+ * pn_47_32
+ *             Bits [47:32] of the PN number.   See description for
+ *             pn_31_0.  The remaining PN fields are in the rx_msdu_end
+ *             descriptor
+ *
+ * pn
+ *             Use this field to access the pn without worrying about
+ *             byte-order and bitmasking/bitshifting.
+ *
+ * directed
+ *             See definition in RX attention descriptor
+ *
+ * reserved_2
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * tid
+ *             The TID field in the QoS control field
+ */
+
+#define RX_MPDU_END_INFO0_RESERVED_0_MASK     0x00001fff
+#define RX_MPDU_END_INFO0_RESERVED_0_LSB      0
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_MASK 0x0fff0000
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_LSB  16
+#define RX_MPDU_END_INFO0_OVERFLOW_ERR        (1 << 13)
+#define RX_MPDU_END_INFO0_LAST_MPDU           (1 << 14)
+#define RX_MPDU_END_INFO0_POST_DELIM_ERR      (1 << 15)
+#define RX_MPDU_END_INFO0_MPDU_LENGTH_ERR     (1 << 28)
+#define RX_MPDU_END_INFO0_TKIP_MIC_ERR        (1 << 29)
+#define RX_MPDU_END_INFO0_DECRYPT_ERR         (1 << 30)
+#define RX_MPDU_END_INFO0_FCS_ERR             (1 << 31)
+
+struct rx_mpdu_end {
+       __le32 info0;
+} __packed;
+
+/*
+ * reserved_0
+ *             Reserved
+ *
+ * overflow_err
+ *             PCU Receive FIFO does not have enough space to store the
+ *             full receive packet.  Enough space is reserved in the
+ *             receive FIFO for the status is written.  This MPDU remaining
+ *             packets in the PPDU will be filtered and no Ack response
+ *             will be transmitted.
+ *
+ * last_mpdu
+ *             Indicates that this is the last MPDU of a PPDU.
+ *
+ * post_delim_err
+ *             Indicates that a delimiter FCS error occurred after this
+ *             MPDU before the next MPDU.  Only valid when last_msdu is
+ *             set.
+ *
+ * post_delim_cnt
+ *             Count of the delimiters after this MPDU.  This requires the
+ *             last MPDU to be held until all the EOF descriptors have been
+ *             received.  This may be inefficient in the future when
+ *             ML-MIMO is used.  Only valid when last_mpdu is set.
+ *
+ * mpdu_length_err
+ *             See definition in RX attention descriptor
+ *
+ * tkip_mic_err
+ *             See definition in RX attention descriptor
+ *
+ * decrypt_err
+ *             See definition in RX attention descriptor
+ *
+ * fcs_err
+ *             See definition in RX attention descriptor
+ */
+
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_MASK    0x00003fff
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_LSB     0
+#define RX_MSDU_START_INFO0_IP_OFFSET_MASK      0x000fc000
+#define RX_MSDU_START_INFO0_IP_OFFSET_LSB       14
+#define RX_MSDU_START_INFO0_RING_MASK_MASK      0x00f00000
+#define RX_MSDU_START_INFO0_RING_MASK_LSB       20
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_MASK 0x7f000000
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_LSB  24
+
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_MASK    0x000000ff
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_LSB     0
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_MASK   0x00000300
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_LSB    8
+#define RX_MSDU_START_INFO1_SA_IDX_MASK         0x07ff0000
+#define RX_MSDU_START_INFO1_SA_IDX_LSB          16
+#define RX_MSDU_START_INFO1_IPV4_PROTO          (1 << 10)
+#define RX_MSDU_START_INFO1_IPV6_PROTO          (1 << 11)
+#define RX_MSDU_START_INFO1_TCP_PROTO           (1 << 12)
+#define RX_MSDU_START_INFO1_UDP_PROTO           (1 << 13)
+#define RX_MSDU_START_INFO1_IP_FRAG             (1 << 14)
+#define RX_MSDU_START_INFO1_TCP_ONLY_ACK        (1 << 15)
+
+enum rx_msdu_decap_format {
+       RX_MSDU_DECAP_RAW           = 0,
+       RX_MSDU_DECAP_NATIVE_WIFI   = 1,
+       RX_MSDU_DECAP_ETHERNET2_DIX = 2,
+       RX_MSDU_DECAP_8023_SNAP_LLC = 3
+};
+
+struct rx_msdu_start {
+       __le32 info0; /* %RX_MSDU_START_INFO0_ */
+       __le32 flow_id_crc;
+       __le32 info1; /* %RX_MSDU_START_INFO1_ */
+} __packed;
+
+/*
+ * msdu_length
+ *             MSDU length in bytes after decapsulation.  This field is
+ *             still valid for MPDU frames without A-MSDU.  It still
+ *             represents MSDU length after decapsulation
+ *
+ * ip_offset
+ *             Indicates the IP offset in bytes from the start of the
+ *             packet after decapsulation.  Only valid if ipv4_proto or
+ *             ipv6_proto is set.
+ *
+ * ring_mask
+ *             Indicates the destination RX rings for this MSDU.
+ *
+ * tcp_udp_offset
+ *             Indicates the offset in bytes to the start of TCP or UDP
+ *             header from the start of the IP header after decapsulation.
+ *             Only valid if tcp_prot or udp_prot is set.  The value 0
+ *             indicates that the offset is longer than 127 bytes.
+ *
+ * reserved_0c
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * flow_id_crc
+ *             The flow_id_crc runs CRC32 on the following information:
+ *             IPv4 option: dest_addr[31:0], src_addr [31:0], {24'b0,
+ *             protocol[7:0]}.
+ *             IPv6 option: dest_addr[127:0], src_addr [127:0], {24'b0,
+ *             next_header[7:0]}
+ *             UDP case: sort_port[15:0], dest_port[15:0]
+ *             TCP case: sort_port[15:0], dest_port[15:0],
+ *             {header_length[3:0], 6'b0, flags[5:0], window_size[15:0]},
+ *             {16'b0, urgent_ptr[15:0]}, all options except 32-bit
+ *             timestamp.
+ *
+ * msdu_number
+ *             Indicates the MSDU number within a MPDU.  This value is
+ *             reset to zero at the start of each MPDU.  If the number of
+ *             MSDU exceeds 255 this number will wrap using modulo 256.
+ *
+ * decap_format
+ *             Indicates the format after decapsulation:
+ *             0: RAW: No decapsulation
+ *             1: Native WiFi
+ *             2: Ethernet 2 (DIX)
+ *             3: 802.3 (SNAP/LLC)
+ *
+ * ipv4_proto
+ *             Set if L2 layer indicates IPv4 protocol.
+ *
+ * ipv6_proto
+ *             Set if L2 layer indicates IPv6 protocol.
+ *
+ * tcp_proto
+ *             Set if the ipv4_proto or ipv6_proto are set and the IP
+ *             protocol indicates TCP.
+ *
+ * udp_proto
+ *             Set if the ipv4_proto or ipv6_proto are set and the IP
+ *                     protocol indicates UDP.
+ *
+ * ip_frag
+ *             Indicates that either the IP More frag bit is set or IP frag
+ *             number is non-zero.  If set indicates that this is a
+ *             fragmented IP packet.
+ *
+ * tcp_only_ack
+ *             Set if only the TCP Ack bit is set in the TCP flags and if
+ *             the TCP payload is 0.
+ *
+ * sa_idx
+ *             The offset in the address table which matches the MAC source
+ *             address.
+ *
+ * reserved_2b
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_MASK 0x00003fff
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB  0
+#define RX_MSDU_END_INFO0_FIRST_MSDU                (1 << 14)
+#define RX_MSDU_END_INFO0_LAST_MSDU                 (1 << 15)
+#define RX_MSDU_END_INFO0_PRE_DELIM_ERR             (1 << 30)
+#define RX_MSDU_END_INFO0_RESERVED_3B               (1 << 31)
+
+struct rx_msdu_end {
+       __le16 ip_hdr_cksum;
+       __le16 tcp_hdr_cksum;
+       u8 key_id_octet;
+       u8 classification_filter;
+       u8 wapi_pn[10];
+       __le32 info0;
+} __packed;
+
+/*
+ *ip_hdr_chksum
+ *             This can include the IP header checksum or the pseudo header
+ *             checksum used by TCP/UDP checksum.
+ *
+ *tcp_udp_chksum
+ *             The value of the computed TCP/UDP checksum.  A mode bit
+ *             selects whether this checksum is the full checksum or the
+ *             partial checksum which does not include the pseudo header.
+ *
+ *key_id_octet
+ *             The key ID octet from the IV.  Only valid when first_msdu is
+ *             set.
+ *
+ *classification_filter
+ *             Indicates the number classification filter rule
+ *
+ *ext_wapi_pn_63_48
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [63:48] (pn6 and pn7).  The
+ *             WAPI PN bits [63:0] are in the pn field of the rx_mpdu_start
+ *             descriptor.
+ *
+ *ext_wapi_pn_95_64
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [95:64] (pn8, pn9, pn10 and
+ *             pn11).
+ *
+ *ext_wapi_pn_127_96
+ *             Extension PN (packet number) which is only used by WAPI.
+ *             This corresponds to WAPI PN bits [127:96] (pn12, pn13, pn14,
+ *             pn15).
+ *
+ *reported_mpdu_length
+ *             MPDU length before decapsulation.  Only valid when
+ *             first_msdu is set.  This field is taken directly from the
+ *             length field of the A-MPDU delimiter or the preamble length
+ *             field for non-A-MPDU frames.
+ *
+ *first_msdu
+ *             Indicates the first MSDU of A-MSDU.  If both first_msdu and
+ *             last_msdu are set in the MSDU then this is a non-aggregated
+ *             MSDU frame: normal MPDU.  Interior MSDU in an A-MSDU shall
+ *             have both first_mpdu and last_mpdu bits set to 0.
+ *
+ *last_msdu
+ *             Indicates the last MSDU of the A-MSDU.  MPDU end status is
+ *             only valid when last_msdu is set.
+ *
+ *reserved_3a
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ *
+ *pre_delim_err
+ *             Indicates that the first delimiter had a FCS failure.  Only
+ *             valid when first_mpdu and first_msdu are set.
+ *
+ *reserved_3b
+ *             Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_PPDU_START_SIG_RATE_SELECT_OFDM 0
+#define RX_PPDU_START_SIG_RATE_SELECT_CCK  1
+
+#define RX_PPDU_START_SIG_RATE_OFDM_48 0
+#define RX_PPDU_START_SIG_RATE_OFDM_24 1
+#define RX_PPDU_START_SIG_RATE_OFDM_12 2
+#define RX_PPDU_START_SIG_RATE_OFDM_6  3
+#define RX_PPDU_START_SIG_RATE_OFDM_54 4
+#define RX_PPDU_START_SIG_RATE_OFDM_36 5
+#define RX_PPDU_START_SIG_RATE_OFDM_18 6
+#define RX_PPDU_START_SIG_RATE_OFDM_9  7
+
+#define RX_PPDU_START_SIG_RATE_CCK_LP_11  0
+#define RX_PPDU_START_SIG_RATE_CCK_LP_5_5 1
+#define RX_PPDU_START_SIG_RATE_CCK_LP_2   2
+#define RX_PPDU_START_SIG_RATE_CCK_LP_1   3
+#define RX_PPDU_START_SIG_RATE_CCK_SP_11  4
+#define RX_PPDU_START_SIG_RATE_CCK_SP_5_5 5
+#define RX_PPDU_START_SIG_RATE_CCK_SP_2   6
+
+#define HTT_RX_PPDU_START_PREAMBLE_LEGACY        0x04
+#define HTT_RX_PPDU_START_PREAMBLE_HT            0x08
+#define HTT_RX_PPDU_START_PREAMBLE_HT_WITH_TXBF  0x09
+#define HTT_RX_PPDU_START_PREAMBLE_VHT           0x0C
+#define HTT_RX_PPDU_START_PREAMBLE_VHT_WITH_TXBF 0x0D
+
+#define RX_PPDU_START_INFO0_IS_GREENFIELD (1 << 0)
+
+#define RX_PPDU_START_INFO1_L_SIG_RATE_MASK    0x0000000f
+#define RX_PPDU_START_INFO1_L_SIG_RATE_LSB     0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_MASK  0x0001ffe0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_LSB   5
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_MASK    0x00fc0000
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_LSB     18
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_MASK 0xff000000
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_LSB  24
+#define RX_PPDU_START_INFO1_L_SIG_RATE_SELECT  (1 << 4)
+#define RX_PPDU_START_INFO1_L_SIG_PARITY       (1 << 17)
+
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_MASK 0x00ffffff
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_LSB  0
+
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_MASK 0x00ffffff
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_LSB  0
+#define RX_PPDU_START_INFO3_TXBF_H_INFO             (1 << 24)
+
+#define RX_PPDU_START_INFO4_VHT_SIG_B_MASK 0x1fffffff
+#define RX_PPDU_START_INFO4_VHT_SIG_B_LSB  0
+
+#define RX_PPDU_START_INFO5_SERVICE_MASK 0x0000ffff
+#define RX_PPDU_START_INFO5_SERVICE_LSB  0
+
+struct rx_ppdu_start {
+       struct {
+               u8 pri20_mhz;
+               u8 ext20_mhz;
+               u8 ext40_mhz;
+               u8 ext80_mhz;
+       } rssi_chains[4];
+       u8 rssi_comb;
+       __le16 rsvd0;
+       u8 info0; /* %RX_PPDU_START_INFO0_ */
+       __le32 info1; /* %RX_PPDU_START_INFO1_ */
+       __le32 info2; /* %RX_PPDU_START_INFO2_ */
+       __le32 info3; /* %RX_PPDU_START_INFO3_ */
+       __le32 info4; /* %RX_PPDU_START_INFO4_ */
+       __le32 info5; /* %RX_PPDU_START_INFO5_ */
+} __packed;
+
+/*
+ * rssi_chain0_pri20
+ *             RSSI of RX PPDU on chain 0 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec20
+ *             RSSI of RX PPDU on chain 0 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec40
+ *             RSSI of RX PPDU on chain 0 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec80
+ *             RSSI of RX PPDU on chain 0 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_pri20
+ *             RSSI of RX PPDU on chain 1 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec20
+ *             RSSI of RX PPDU on chain 1 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec40
+ *             RSSI of RX PPDU on chain 1 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec80
+ *             RSSI of RX PPDU on chain 1 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_pri20
+ *             RSSI of RX PPDU on chain 2 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec20
+ *             RSSI of RX PPDU on chain 2 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec40
+ *             RSSI of RX PPDU on chain 2 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec80
+ *             RSSI of RX PPDU on chain 2 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_pri20
+ *             RSSI of RX PPDU on chain 3 of primary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec20
+ *             RSSI of RX PPDU on chain 3 of secondary 20 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec40
+ *             RSSI of RX PPDU on chain 3 of secondary 40 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec80
+ *             RSSI of RX PPDU on chain 3 of secondary 80 MHz bandwidth.
+ *             Value of 0x80 indicates invalid.
+ *
+ * rssi_comb
+ *             The combined RSSI of RX PPDU of all active chains and
+ *             bandwidths.  Value of 0x80 indicates invalid.
+ *
+ * reserved_4a
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * is_greenfield
+ *             Do we really support this?
+ *
+ * reserved_4b
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * l_sig_rate
+ *             If l_sig_rate_select is 0:
+ *             0x8: OFDM 48 Mbps
+ *             0x9: OFDM 24 Mbps
+ *             0xA: OFDM 12 Mbps
+ *             0xB: OFDM 6 Mbps
+ *             0xC: OFDM 54 Mbps
+ *             0xD: OFDM 36 Mbps
+ *             0xE: OFDM 18 Mbps
+ *             0xF: OFDM 9 Mbps
+ *             If l_sig_rate_select is 1:
+ *             0x8: CCK 11 Mbps long preamble
+ *             0x9: CCK 5.5 Mbps long preamble
+ *             0xA: CCK 2 Mbps long preamble
+ *             0xB: CCK 1 Mbps long preamble
+ *             0xC: CCK 11 Mbps short preamble
+ *             0xD: CCK 5.5 Mbps short preamble
+ *             0xE: CCK 2 Mbps short preamble
+ *
+ * l_sig_rate_select
+ *             Legacy signal rate select.  If set then l_sig_rate indicates
+ *             CCK rates.  If clear then l_sig_rate indicates OFDM rates.
+ *
+ * l_sig_length
+ *             Length of legacy frame in octets.
+ *
+ * l_sig_parity
+ *             Odd parity over l_sig_rate and l_sig_length
+ *
+ * l_sig_tail
+ *             Tail bits for Viterbi decoder
+ *
+ * preamble_type
+ *             Indicates the type of preamble ahead:
+ *             0x4: Legacy (OFDM/CCK)
+ *             0x8: HT
+ *             0x9: HT with TxBF
+ *             0xC: VHT
+ *             0xD: VHT with TxBF
+ *             0x80 - 0xFF: Reserved for special baseband data types such
+ *             as radar and spectral scan.
+ *
+ * ht_sig_vht_sig_a_1
+ *             If preamble_type == 0x8 or 0x9
+ *             HT-SIG (first 24 bits)
+ *             If preamble_type == 0xC or 0xD
+ *             VHT-SIG A (first 24 bits)
+ *             Else
+ *             Reserved
+ *
+ * reserved_6
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ht_sig_vht_sig_a_2
+ *             If preamble_type == 0x8 or 0x9
+ *             HT-SIG (last 24 bits)
+ *             If preamble_type == 0xC or 0xD
+ *             VHT-SIG A (last 24 bits)
+ *             Else
+ *             Reserved
+ *
+ * txbf_h_info
+ *             Indicates that the packet data carries H information which
+ *             is used for TxBF debug.
+ *
+ * reserved_7
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * vht_sig_b
+ *             WiFi 1.0 and WiFi 2.0 will likely have this field to be all
+ *             0s since the BB does not plan on decoding VHT SIG-B.
+ *
+ * reserved_8
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * service
+ *             Service field from BB for OFDM, HT and VHT packets.  CCK
+ *             packets will have service field of 0.
+ *
+ * reserved_9
+ *             Reserved: HW should fill with 0, FW should ignore.
+*/
+
+
+#define RX_PPDU_END_FLAGS_PHY_ERR             (1 << 0)
+#define RX_PPDU_END_FLAGS_RX_LOCATION         (1 << 1)
+#define RX_PPDU_END_FLAGS_TXBF_H_INFO         (1 << 2)
+
+#define RX_PPDU_END_INFO0_RX_ANTENNA_MASK     0x00ffffff
+#define RX_PPDU_END_INFO0_RX_ANTENNA_LSB      0
+#define RX_PPDU_END_INFO0_FLAGS_TX_HT_VHT_ACK (1 << 24)
+#define RX_PPDU_END_INFO0_BB_CAPTURED_CHANNEL (1 << 25)
+
+#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
+
+struct rx_ppdu_end {
+       __le32 evm_p0;
+       __le32 evm_p1;
+       __le32 evm_p2;
+       __le32 evm_p3;
+       __le32 evm_p4;
+       __le32 evm_p5;
+       __le32 evm_p6;
+       __le32 evm_p7;
+       __le32 evm_p8;
+       __le32 evm_p9;
+       __le32 evm_p10;
+       __le32 evm_p11;
+       __le32 evm_p12;
+       __le32 evm_p13;
+       __le32 evm_p14;
+       __le32 evm_p15;
+       __le32 tsf_timestamp;
+       __le32 wb_timestamp;
+       u8 locationing_timestamp;
+       u8 phy_err_code;
+       __le16 flags; /* %RX_PPDU_END_FLAGS_ */
+       __le32 info0; /* %RX_PPDU_END_INFO0_ */
+       __le16 bb_length;
+       __le16 info1; /* %RX_PPDU_END_INFO1_ */
+} __packed;
+
+/*
+ * evm_p0
+ *             EVM for pilot 0.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p1
+ *             EVM for pilot 1.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p2
+ *             EVM for pilot 2.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p3
+ *             EVM for pilot 3.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p4
+ *             EVM for pilot 4.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p5
+ *             EVM for pilot 5.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p6
+ *             EVM for pilot 6.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p7
+ *             EVM for pilot 7.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p8
+ *             EVM for pilot 8.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p9
+ *             EVM for pilot 9.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p10
+ *             EVM for pilot 10.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p11
+ *             EVM for pilot 11.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p12
+ *             EVM for pilot 12.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p13
+ *             EVM for pilot 13.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p14
+ *             EVM for pilot 14.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p15
+ *             EVM for pilot 15.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * tsf_timestamp
+ *             Receive TSF timestamp sampled on the rising edge of
+ *             rx_clear.  For PHY errors this may be the current TSF when
+ *             phy_error is asserted if the rx_clear does not assert before
+ *             the end of the PHY error.
+ *
+ * wb_timestamp
+ *             WLAN/BT timestamp is a 1 usec resolution timestamp which
+ *             does not get updated based on receive beacon like TSF.  The
+ *             same rules for capturing tsf_timestamp are used to capture
+ *             the wb_timestamp.
+ *
+ * locationing_timestamp
+ *             Timestamp used for locationing.  This timestamp is used to
+ *             indicate fractions of usec.  For example if the MAC clock is
+ *             running at 80 MHz, the timestamp will increment every 12.5
+ *             nsec.  The value starts at 0 and increments to 79 and
+ *             returns to 0 and repeats.  This information is valid for
+ *             every PPDU.  This information can be used in conjunction
+ *             with wb_timestamp to capture large delta times.
+ *
+ * phy_err_code
+ *             See the 1.10.8.1.2 for the list of the PHY error codes.
+ *
+ * phy_err
+ *             Indicates a PHY error was detected for this PPDU.
+ *
+ * rx_location
+ *             Indicates that location information was requested.
+ *
+ * txbf_h_info
+ *             Indicates that the packet data carries H information which
+ *             is used for TxBF debug.
+ *
+ * reserved_18
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * rx_antenna
+ *             Receive antenna value
+ *
+ * tx_ht_vht_ack
+ *             Indicates that a HT or VHT Ack/BA frame was transmitted in
+ *             response to this receive packet.
+ *
+ * bb_captured_channel
+ *             Indicates that the BB has captured a channel dump.  FW can
+ *             then read the channel dump memory.  This may indicate that
+ *             the channel was captured either based on PCU setting the
+ *             capture_channel bit  BB descriptor or FW setting the
+ *             capture_channel mode bit.
+ *
+ * reserved_19
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * bb_length
+ *             Indicates the number of bytes of baseband information for
+ *             PPDUs where the BB descriptor preamble type is 0x80 to 0xFF
+ *             which indicates that this is not a normal PPDU but rather
+ *             contains baseband debug information.
+ *
+ * reserved_20
+ *             Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ppdu_done
+ *             PPDU end status is only valid when ppdu_done bit is set.
+ *             Every time HW sets this bit in memory FW/SW must clear this
+ *             bit in memory.  FW will initialize all the ppdu_done dword
+ *             to 0.
+*/
+
+#define FW_RX_DESC_INFO0_DISCARD  (1 << 0)
+#define FW_RX_DESC_INFO0_FORWARD  (1 << 1)
+#define FW_RX_DESC_INFO0_INSPECT  (1 << 5)
+#define FW_RX_DESC_INFO0_EXT_MASK 0xC0
+#define FW_RX_DESC_INFO0_EXT_LSB  6
+
+struct fw_rx_desc_base {
+       u8 info0;
+} __packed;
+
+#endif /* _RX_DESC_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
new file mode 100644 (file)
index 0000000..be7ba1e
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 __TARGADDRS_H__
+#define __TARGADDRS_H__
+
+/*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure.  It must match the address of the _host_interest
+ * symbol (see linker script).
+ *
+ * Host Interest is shared between Host and Target in order to coordinate
+ * between the two, and is intended to remain constant (with additions only
+ * at the end) across software releases.
+ *
+ * All addresses are available here so that it's possible to
+ * write a single binary that works with all Target Types.
+ * May be used in assembler code as well as C.
+ */
+#define QCA988X_HOST_INTEREST_ADDRESS    0x00400800
+#define HOST_INTEREST_MAX_SIZE          0x200
+
+/*
+ * These are items that the Host may need to access via BMI or via the
+ * Diagnostic Window. The position of items in this structure must remain
+ * constant across firmware revisions! Types for each item must be fixed
+ * size across target and host platforms. More items may be added at the end.
+ */
+struct host_interest {
+       /*
+        * Pointer to application-defined area, if any.
+        * Set by Target application during startup.
+        */
+       u32 hi_app_host_interest;                       /* 0x00 */
+
+       /* Pointer to register dump area, valid after Target crash. */
+       u32 hi_failure_state;                           /* 0x04 */
+
+       /* Pointer to debug logging header */
+       u32 hi_dbglog_hdr;                              /* 0x08 */
+
+       u32 hi_unused0c;                                /* 0x0c */
+
+       /*
+        * General-purpose flag bits, similar to SOC_OPTION_* flags.
+        * Can be used by application rather than by OS.
+        */
+       u32 hi_option_flag;                             /* 0x10 */
+
+       /*
+        * Boolean that determines whether or not to
+        * display messages on the serial port.
+        */
+       u32 hi_serial_enable;                           /* 0x14 */
+
+       /* Start address of DataSet index, if any */
+       u32 hi_dset_list_head;                          /* 0x18 */
+
+       /* Override Target application start address */
+       u32 hi_app_start;                               /* 0x1c */
+
+       /* Clock and voltage tuning */
+       u32 hi_skip_clock_init;                         /* 0x20 */
+       u32 hi_core_clock_setting;                      /* 0x24 */
+       u32 hi_cpu_clock_setting;                       /* 0x28 */
+       u32 hi_system_sleep_setting;                    /* 0x2c */
+       u32 hi_xtal_control_setting;                    /* 0x30 */
+       u32 hi_pll_ctrl_setting_24ghz;                  /* 0x34 */
+       u32 hi_pll_ctrl_setting_5ghz;                   /* 0x38 */
+       u32 hi_ref_voltage_trim_setting;                /* 0x3c */
+       u32 hi_clock_info;                              /* 0x40 */
+
+       /* Host uses BE CPU or not */
+       u32 hi_be;                                      /* 0x44 */
+
+       u32 hi_stack;   /* normal stack */                      /* 0x48 */
+       u32 hi_err_stack; /* error stack */             /* 0x4c */
+       u32 hi_desired_cpu_speed_hz;                    /* 0x50 */
+
+       /* Pointer to Board Data  */
+       u32 hi_board_data;                              /* 0x54 */
+
+       /*
+        * Indication of Board Data state:
+        *    0: board data is not yet initialized.
+        *    1: board data is initialized; unknown size
+        *   >1: number of bytes of initialized board data
+        */
+       u32 hi_board_data_initialized;                  /* 0x58 */
+
+       u32 hi_dset_ram_index_table;                    /* 0x5c */
+
+       u32 hi_desired_baud_rate;                       /* 0x60 */
+       u32 hi_dbglog_config;                           /* 0x64 */
+       u32 hi_end_ram_reserve_sz;                      /* 0x68 */
+       u32 hi_mbox_io_block_sz;                        /* 0x6c */
+
+       u32 hi_num_bpatch_streams;                      /* 0x70 -- unused */
+       u32 hi_mbox_isr_yield_limit;                    /* 0x74 */
+
+       u32 hi_refclk_hz;                               /* 0x78 */
+       u32 hi_ext_clk_detected;                        /* 0x7c */
+       u32 hi_dbg_uart_txpin;                          /* 0x80 */
+       u32 hi_dbg_uart_rxpin;                          /* 0x84 */
+       u32 hi_hci_uart_baud;                           /* 0x88 */
+       u32 hi_hci_uart_pin_assignments;                /* 0x8C */
+
+       u32 hi_hci_uart_baud_scale_val;                 /* 0x90 */
+       u32 hi_hci_uart_baud_step_val;                  /* 0x94 */
+
+       u32 hi_allocram_start;                          /* 0x98 */
+       u32 hi_allocram_sz;                             /* 0x9c */
+       u32 hi_hci_bridge_flags;                        /* 0xa0 */
+       u32 hi_hci_uart_support_pins;                   /* 0xa4 */
+
+       u32 hi_hci_uart_pwr_mgmt_params;                /* 0xa8 */
+
+       /*
+        * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
+        *        [31:16]: wakeup timeout in ms
+        */
+       /* Pointer to extended board Data  */
+       u32 hi_board_ext_data;                          /* 0xac */
+       u32 hi_board_ext_data_config;                   /* 0xb0 */
+       /*
+        * Bit [0]  :   valid
+        * Bit[31:16:   size
+        */
+       /*
+        * hi_reset_flag is used to do some stuff when target reset.
+        * such as restore app_start after warm reset or
+        * preserve host Interest area, or preserve ROM data, literals etc.
+        */
+       u32  hi_reset_flag;                             /* 0xb4 */
+       /* indicate hi_reset_flag is valid */
+       u32  hi_reset_flag_valid;                       /* 0xb8 */
+       u32 hi_hci_uart_pwr_mgmt_params_ext;            /* 0xbc */
+       /* 0xbc - [31:0]: idle timeout in ms */
+       /* ACS flags */
+       u32 hi_acs_flags;                               /* 0xc0 */
+       u32 hi_console_flags;                           /* 0xc4 */
+       u32 hi_nvram_state;                             /* 0xc8 */
+       u32 hi_option_flag2;                            /* 0xcc */
+
+       /* If non-zero, override values sent to Host in WMI_READY event. */
+       u32 hi_sw_version_override;                     /* 0xd0 */
+       u32 hi_abi_version_override;                    /* 0xd4 */
+
+       /*
+        * Percentage of high priority RX traffic to total expected RX traffic
+        * applicable only to ar6004
+        */
+       u32 hi_hp_rx_traffic_ratio;                     /* 0xd8 */
+
+       /* test applications flags */
+       u32 hi_test_apps_related;                       /* 0xdc */
+       /* location of test script */
+       u32 hi_ota_testscript;                          /* 0xe0 */
+       /* location of CAL data */
+       u32 hi_cal_data;                                /* 0xe4 */
+
+       /* Number of packet log buffers */
+       u32 hi_pktlog_num_buffers;                      /* 0xe8 */
+
+       /* wow extension configuration */
+       u32 hi_wow_ext_config;                          /* 0xec */
+       u32 hi_pwr_save_flags;                          /* 0xf0 */
+
+       /* Spatial Multiplexing Power Save (SMPS) options */
+       u32 hi_smps_options;                            /* 0xf4 */
+
+       /* Interconnect-specific state */
+       u32 hi_interconnect_state;                      /* 0xf8 */
+
+       /* Coex configuration flags */
+       u32 hi_coex_config;                             /* 0xfc */
+
+       /* Early allocation support */
+       u32 hi_early_alloc;                             /* 0x100 */
+       /* FW swap field */
+       /*
+        * Bits of this 32bit word will be used to pass specific swap
+        * instruction to FW
+        */
+       /*
+        * Bit 0 -- AP Nart descriptor no swap. When this bit is set
+        * FW will not swap TX descriptor. Meaning packets are formed
+        * on the target processor.
+        */
+       /* Bit 1 - unused */
+       u32 hi_fw_swap;                                 /* 0x104 */
+} __packed;
+
+#define HI_ITEM(item)  offsetof(struct host_interest, item)
+
+/* Bits defined in hi_option_flag */
+
+/* Enable timer workaround */
+#define HI_OPTION_TIMER_WAR         0x01
+/* Limit BMI command credits */
+#define HI_OPTION_BMI_CRED_LIMIT    0x02
+/* Relay Dot11 hdr to/from host */
+#define HI_OPTION_RELAY_DOT11_HDR   0x04
+/* MAC addr method 0-locally administred 1-globally unique addrs */
+#define HI_OPTION_MAC_ADDR_METHOD   0x08
+/* Firmware Bridging */
+#define HI_OPTION_FW_BRIDGE         0x10
+/* Enable CPU profiling */
+#define HI_OPTION_ENABLE_PROFILE    0x20
+/* Disable debug logging */
+#define HI_OPTION_DISABLE_DBGLOG    0x40
+/* Skip Era Tracking */
+#define HI_OPTION_SKIP_ERA_TRACKING 0x80
+/* Disable PAPRD (debug) */
+#define HI_OPTION_PAPRD_DISABLE     0x100
+#define HI_OPTION_NUM_DEV_LSB       0x200
+#define HI_OPTION_NUM_DEV_MSB       0x800
+#define HI_OPTION_DEV_MODE_LSB      0x1000
+#define HI_OPTION_DEV_MODE_MSB      0x8000000
+/* Disable LowFreq Timer Stabilization */
+#define HI_OPTION_NO_LFT_STBL       0x10000000
+/* Skip regulatory scan */
+#define HI_OPTION_SKIP_REG_SCAN     0x20000000
+/*
+ * Do regulatory scan during init before
+ * sending WMI ready event to host
+ */
+#define HI_OPTION_INIT_REG_SCAN     0x40000000
+
+/* REV6: Do not adjust memory map */
+#define HI_OPTION_SKIP_MEMMAP       0x80000000
+
+#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
+
+/* 2 bits of hi_option_flag are used to represent 3 modes */
+#define HI_OPTION_FW_MODE_IBSS    0x0 /* IBSS Mode */
+#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */
+#define HI_OPTION_FW_MODE_AP      0x2 /* AP Mode */
+#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */
+
+/* 2 bits of hi_option flag are usedto represent 4 submodes */
+#define HI_OPTION_FW_SUBMODE_NONE    0x0  /* Normal mode */
+#define HI_OPTION_FW_SUBMODE_P2PDEV  0x1  /* p2p device mode */
+#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */
+#define HI_OPTION_FW_SUBMODE_P2PGO   0x3 /* p2p go mode */
+
+/* Num dev Mask */
+#define HI_OPTION_NUM_DEV_MASK    0x7
+#define HI_OPTION_NUM_DEV_SHIFT   0x9
+
+/* firmware bridging */
+#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
+
+/*
+Fw Mode/SubMode Mask
+|-----------------------------------------------------------------------------|
+|  SUB   |   SUB   |   SUB   |  SUB    |         |         |         |        |
+|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]|
+|  (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)  |
+|-----------------------------------------------------------------------------|
+*/
+#define HI_OPTION_FW_MODE_BITS         0x2
+#define HI_OPTION_FW_MODE_MASK         0x3
+#define HI_OPTION_FW_MODE_SHIFT        0xC
+#define HI_OPTION_ALL_FW_MODE_MASK     0xFF
+
+#define HI_OPTION_FW_SUBMODE_BITS      0x2
+#define HI_OPTION_FW_SUBMODE_MASK      0x3
+#define HI_OPTION_FW_SUBMODE_SHIFT     0x14
+#define HI_OPTION_ALL_FW_SUBMODE_MASK  0xFF00
+#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8
+
+
+/* hi_option_flag2 options */
+#define HI_OPTION_OFFLOAD_AMSDU     0x01
+#define HI_OPTION_DFS_SUPPORT       0x02 /* Enable DFS support */
+#define HI_OPTION_ENABLE_RFKILL     0x04 /* RFKill Enable Feature*/
+#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */
+#define HI_OPTION_EARLY_CFG_DONE    0x10 /* Early configuration is complete */
+
+#define HI_OPTION_RF_KILL_SHIFT     0x2
+#define HI_OPTION_RF_KILL_MASK      0x1
+
+/* hi_reset_flag */
+/* preserve App Start address */
+#define HI_RESET_FLAG_PRESERVE_APP_START         0x01
+/* preserve host interest */
+#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST     0x02
+/* preserve ROM data */
+#define HI_RESET_FLAG_PRESERVE_ROMDATA           0x04
+#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE       0x08
+#define HI_RESET_FLAG_PRESERVE_BOOT_INFO         0x10
+#define HI_RESET_FLAG_WARM_RESET       0x20
+
+/* define hi_fw_swap bits */
+#define HI_DESC_IN_FW_BIT      0x01
+
+/* indicate the reset flag is valid */
+#define HI_RESET_FLAG_IS_VALID  0x12345678
+
+/* ACS is enabled */
+#define HI_ACS_FLAGS_ENABLED        (1 << 0)
+/* Use physical WWAN device */
+#define HI_ACS_FLAGS_USE_WWAN       (1 << 1)
+/* Use test VAP */
+#define HI_ACS_FLAGS_TEST_VAP       (1 << 2)
+
+/*
+ * CONSOLE FLAGS
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   2..0     UART ID (0 = Default)
+ *    3       Baud Select (0 = 9600, 1 = 115200)
+ *   30..4    Reserved
+ *    31      Enable Console
+ *
+ */
+
+#define HI_CONSOLE_FLAGS_ENABLE       (1 << 31)
+#define HI_CONSOLE_FLAGS_UART_MASK    (0x7)
+#define HI_CONSOLE_FLAGS_UART_SHIFT   0
+#define HI_CONSOLE_FLAGS_BAUD_SELECT  (1 << 3)
+
+/* SM power save options */
+#define HI_SMPS_ALLOW_MASK            (0x00000001)
+#define HI_SMPS_MODE_MASK             (0x00000002)
+#define HI_SMPS_MODE_STATIC           (0x00000000)
+#define HI_SMPS_MODE_DYNAMIC          (0x00000002)
+#define HI_SMPS_DISABLE_AUTO_MODE     (0x00000004)
+#define HI_SMPS_DATA_THRESH_MASK      (0x000007f8)
+#define HI_SMPS_DATA_THRESH_SHIFT     (3)
+#define HI_SMPS_RSSI_THRESH_MASK      (0x0007f800)
+#define HI_SMPS_RSSI_THRESH_SHIFT     (11)
+#define HI_SMPS_LOWPWR_CM_MASK        (0x00380000)
+#define HI_SMPS_LOWPWR_CM_SHIFT       (15)
+#define HI_SMPS_HIPWR_CM_MASK         (0x03c00000)
+#define HI_SMPS_HIPWR_CM_SHIFT        (19)
+
+/*
+ * WOW Extension configuration
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   8..0     Size of each WOW pattern (max 511)
+ *   15..9    Number of patterns per list (max 127)
+ *   17..16   Number of lists (max 4)
+ *   30..18   Reserved
+ *   31       Enabled
+ *
+ *  set values (except enable) to zeros for default settings
+ */
+
+#define HI_WOW_EXT_ENABLED_MASK        (1 << 31)
+#define HI_WOW_EXT_NUM_LIST_SHIFT      16
+#define HI_WOW_EXT_NUM_LIST_MASK       (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_NUM_PATTERNS_SHIFT  9
+#define HI_WOW_EXT_NUM_PATTERNS_MASK   (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_PATTERN_SIZE_SHIFT  0
+#define HI_WOW_EXT_PATTERN_SIZE_MASK   (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \
+       ((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \
+               HI_WOW_EXT_NUM_LIST_MASK) | \
+       (((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \
+               HI_WOW_EXT_NUM_PATTERNS_MASK) | \
+       (((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \
+               HI_WOW_EXT_PATTERN_SIZE_MASK))
+
+#define HI_WOW_EXT_GET_NUM_LISTS(config) \
+       (((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \
+       (((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \
+               HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \
+       (((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \
+               HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+/*
+ * Early allocation configuration
+ * Support RAM bank configuration before BMI done and this eases the memory
+ * allocation at very early stage
+ * Bit Range  Meaning
+ * ---------  ----------------------------------
+ * [0:3]      number of bank assigned to be IRAM
+ * [4:15]     reserved
+ * [16:31]    magic number
+ *
+ * Note:
+ * 1. target firmware would check magic number and if it's a match, firmware
+ *    would consider the bits[0:15] are valid and base on that to calculate
+ *    the end of DRAM. Early allocation would be located at that area and
+ *    may be reclaimed when necesary
+ * 2. if no magic number is found, early allocation would happen at "_end"
+ *    symbol of ROM which is located before the app-data and might NOT be
+ *    re-claimable. If this is adopted, link script should keep this in
+ *    mind to avoid data corruption.
+ */
+#define HI_EARLY_ALLOC_MAGIC           0x6d8a
+#define HI_EARLY_ALLOC_MAGIC_MASK      0xffff0000
+#define HI_EARLY_ALLOC_MAGIC_SHIFT     16
+#define HI_EARLY_ALLOC_IRAM_BANKS_MASK 0x0000000f
+#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT        0
+
+#define HI_EARLY_ALLOC_VALID() \
+       ((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \
+       HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC))
+#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \
+       (((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \
+       >> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT)
+
+/*power save flag bit definitions*/
+#define HI_PWR_SAVE_LPL_ENABLED   0x1
+/*b1-b3 reserved*/
+/*b4-b5 : dev0 LPL type : 0 - none
+                         1- Reduce Pwr Search
+                         2- Reduce Pwr Listen*/
+/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/
+#define HI_PWR_SAVE_LPL_DEV0_LSB   4
+#define HI_PWR_SAVE_LPL_DEV_MASK   0x3
+/*power save related utility macros*/
+#define HI_LPL_ENABLED() \
+       ((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED))
+#define HI_DEV_LPL_TYPE_GET(_devix) \
+       (HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \
+        (HI_PWR_SAVE_LPL_DEV0_LSB + (_devix)*2)))
+
+#define HOST_INTEREST_SMPS_IS_ALLOWED() \
+       ((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK))
+
+/* Reserve 1024 bytes for extended board data */
+#define QCA988X_BOARD_DATA_SZ     7168
+#define QCA988X_BOARD_EXT_DATA_SZ 0
+
+#endif /* __TARGADDRS_H__ */
diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c
new file mode 100644 (file)
index 0000000..4a31e2c
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
new file mode 100644 (file)
index 0000000..85e806b
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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.
+ */
+
+#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+
+#include <linux/tracepoint.h>
+
+#define _TRACE_H_
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_ATH10K_TRACING)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ath10k
+
+#define ATH10K_MSG_MAX 200
+
+DECLARE_EVENT_CLASS(ath10k_log_event,
+       TP_PROTO(struct va_format *vaf),
+       TP_ARGS(vaf),
+       TP_STRUCT__entry(
+               __dynamic_array(char, msg, ATH10K_MSG_MAX)
+       ),
+       TP_fast_assign(
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH10K_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH10K_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_err,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_warn,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_info,
+            TP_PROTO(struct va_format *vaf),
+            TP_ARGS(vaf)
+);
+
+TRACE_EVENT(ath10k_log_dbg,
+       TP_PROTO(unsigned int level, struct va_format *vaf),
+       TP_ARGS(level, vaf),
+       TP_STRUCT__entry(
+               __field(unsigned int, level)
+               __dynamic_array(char, msg, ATH10K_MSG_MAX)
+       ),
+       TP_fast_assign(
+               __entry->level = level;
+               WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+                                      ATH10K_MSG_MAX,
+                                      vaf->fmt,
+                                      *vaf->va) >= ATH10K_MSG_MAX);
+       ),
+       TP_printk("%s", __get_str(msg))
+);
+
+TRACE_EVENT(ath10k_log_dbg_dump,
+       TP_PROTO(const char *msg, const char *prefix,
+                const void *buf, size_t buf_len),
+
+       TP_ARGS(msg, prefix, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __string(msg, msg)
+               __string(prefix, prefix)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __assign_str(msg, msg);
+               __assign_str(prefix, prefix);
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "%s/%s\n", __get_str(prefix), __get_str(msg)
+       )
+);
+
+TRACE_EVENT(ath10k_wmi_cmd,
+       TP_PROTO(int id, void *buf, size_t buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zu",
+               __entry->id,
+               __entry->buf_len
+       )
+);
+
+TRACE_EVENT(ath10k_wmi_event,
+       TP_PROTO(int id, void *buf, size_t buf_len),
+
+       TP_ARGS(id, buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(unsigned int, id)
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->id = id;
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "id %d len %zu",
+               __entry->id,
+               __entry->buf_len
+       )
+);
+
+#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
+
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
new file mode 100644 (file)
index 0000000..68b6fae
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 "core.h"
+#include "txrx.h"
+#include "htt.h"
+#include "mac.h"
+#include "debug.h"
+
+static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
+{
+       if (!ATH10K_SKB_CB(skb)->htt.is_offchan)
+               return;
+
+       /* If the original wait_for_completion() timed out before
+        * {data,mgmt}_tx_completed() was called then we could complete
+        * offchan_tx_completed for a different skb. Prevent this by using
+        * offchan_tx_skb. */
+       spin_lock_bh(&ar->data_lock);
+       if (ar->offchan_tx_skb != skb) {
+               ath10k_warn("completed old offchannel frame\n");
+               goto out;
+       }
+
+       complete(&ar->offchan_tx_completed);
+       ar->offchan_tx_skb = NULL; /* just for sanity */
+
+       ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb);
+out:
+       spin_unlock_bh(&ar->data_lock);
+}
+
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc)
+{
+       struct device *dev = htt->ar->dev;
+       struct ieee80211_tx_info *info;
+       struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag;
+       struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu;
+       int ret;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0)
+               return;
+
+       ATH10K_SKB_CB(txdesc)->htt.refcount--;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+               return;
+
+       if (txfrag) {
+               ret = ath10k_skb_unmap(dev, txfrag);
+               if (ret)
+                       ath10k_warn("txfrag unmap failed (%d)\n", ret);
+
+               dev_kfree_skb_any(txfrag);
+       }
+
+       ret = ath10k_skb_unmap(dev, msdu);
+       if (ret)
+               ath10k_warn("data skb unmap failed (%d)\n", ret);
+
+       ath10k_report_offchan_tx(htt->ar, msdu);
+
+       info = IEEE80211_SKB_CB(msdu);
+       memset(&info->status, 0, sizeof(info->status));
+
+       if (ATH10K_SKB_CB(txdesc)->htt.discard) {
+               ieee80211_free_txskb(htt->ar->hw, msdu);
+               goto exit;
+       }
+
+       if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+               info->flags |= IEEE80211_TX_STAT_ACK;
+
+       if (ATH10K_SKB_CB(txdesc)->htt.no_ack)
+               info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+       ieee80211_tx_status(htt->ar->hw, msdu);
+       /* we do not own the msdu anymore */
+
+exit:
+       spin_lock_bh(&htt->tx_lock);
+       htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
+       ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
+       __ath10k_htt_tx_dec_pending(htt);
+       if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx))
+               wake_up(&htt->empty_tx_wq);
+       spin_unlock_bh(&htt->tx_lock);
+
+       dev_kfree_skb_any(txdesc);
+}
+
+void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+                             const struct htt_tx_done *tx_done)
+{
+       struct sk_buff *txdesc;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n",
+                  tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack);
+
+       if (tx_done->msdu_id >= htt->max_num_pending_tx) {
+               ath10k_warn("warning: msdu_id %d too big, ignoring\n",
+                           tx_done->msdu_id);
+               return;
+       }
+
+       txdesc = htt->pending_tx[tx_done->msdu_id];
+
+       ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard;
+       ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack;
+
+       ath10k_txrx_tx_unref(htt, txdesc);
+}
+
+static const u8 rx_legacy_rate_idx[] = {
+       3,      /* 0x00  - 11Mbps  */
+       2,      /* 0x01  - 5.5Mbps */
+       1,      /* 0x02  - 2Mbps   */
+       0,      /* 0x03  - 1Mbps   */
+       3,      /* 0x04  - 11Mbps  */
+       2,      /* 0x05  - 5.5Mbps */
+       1,      /* 0x06  - 2Mbps   */
+       0,      /* 0x07  - 1Mbps   */
+       10,     /* 0x08  - 48Mbps  */
+       8,      /* 0x09  - 24Mbps  */
+       6,      /* 0x0A  - 12Mbps  */
+       4,      /* 0x0B  - 6Mbps   */
+       11,     /* 0x0C  - 54Mbps  */
+       9,      /* 0x0D  - 36Mbps  */
+       7,      /* 0x0E  - 18Mbps  */
+       5,      /* 0x0F  - 9Mbps   */
+};
+
+static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info,
+                            enum ieee80211_band band,
+                            struct ieee80211_rx_status *status)
+{
+       u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
+       u8 info0 = info->rate.info0;
+       u32 info1 = info->rate.info1;
+       u32 info2 = info->rate.info2;
+       u8 preamble = 0;
+
+       /* Check if valid fields */
+       if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+               return;
+
+       preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+
+       switch (preamble) {
+       case HTT_RX_LEGACY:
+               cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
+               rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+               rate_idx = 0;
+
+               if (rate < 0x08 || rate > 0x0F)
+                       break;
+
+               switch (band) {
+               case IEEE80211_BAND_2GHZ:
+                       if (cck)
+                               rate &= ~BIT(3);
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       break;
+               case IEEE80211_BAND_5GHZ:
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       /* We are using same rate table registering
+                          HW - ath10k_rates[]. In case of 5GHz skip
+                          CCK rates, so -4 here */
+                       rate_idx -= 4;
+                       break;
+               default:
+                       break;
+               }
+
+               status->rate_idx = rate_idx;
+               break;
+       case HTT_RX_HT:
+       case HTT_RX_HT_WITH_TXBF:
+               /* HT-SIG - Table 20-11 in info1 and info2 */
+               mcs = info1 & 0x1F;
+               nss = mcs >> 3;
+               bw = (info1 >> 7) & 1;
+               sgi = (info2 >> 7) & 1;
+
+               status->rate_idx = mcs;
+               status->flag |= RX_FLAG_HT;
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+               if (bw)
+                       status->flag |= RX_FLAG_40MHZ;
+               break;
+       case HTT_RX_VHT:
+       case HTT_RX_VHT_WITH_TXBF:
+               /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+                  TODO check this */
+               mcs = (info2 >> 4) & 0x0F;
+               nss = (info1 >> 10) & 0x07;
+               bw = info1 & 3;
+               sgi = info2 & 1;
+
+               status->rate_idx = mcs;
+               status->vht_nss = nss;
+
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+
+               switch (bw) {
+               /* 20MHZ */
+               case 0:
+                       break;
+               /* 40MHZ */
+               case 1:
+                       status->flag |= RX_FLAG_40MHZ;
+                       break;
+               /* 80MHZ */
+               case 2:
+                       status->flag |= RX_FLAG_80MHZ;
+               }
+
+               status->flag |= RX_FLAG_VHT;
+               break;
+       default:
+               break;
+       }
+}
+
+void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
+{
+       struct ieee80211_rx_status *status;
+       struct ieee80211_channel *ch;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data;
+
+       status = IEEE80211_SKB_RXCB(info->skb);
+       memset(status, 0, sizeof(*status));
+
+       if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+               hdr->frame_control = __cpu_to_le16(
+                               __le16_to_cpu(hdr->frame_control) &
+                               ~IEEE80211_FCTL_PROTECTED);
+       }
+
+       if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       if (info->fcs_err)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+       status->signal = info->signal;
+
+       spin_lock_bh(&ar->data_lock);
+       ch = ar->scan_channel;
+       if (!ch)
+               ch = ar->rx_channel;
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!ch) {
+               ath10k_warn("no channel configured; ignoring frame!\n");
+               dev_kfree_skb_any(info->skb);
+               return;
+       }
+
+       process_rx_rates(ar, info, ch->band, status);
+       status->band = ch->band;
+       status->freq = ch->center_freq;
+
+       ath10k_dbg(ATH10K_DBG_DATA,
+                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n",
+                  info->skb,
+                  info->skb->len,
+                  status->flag == 0 ? "legacy" : "",
+                  status->flag & RX_FLAG_HT ? "ht" : "",
+                  status->flag & RX_FLAG_VHT ? "vht" : "",
+                  status->flag & RX_FLAG_40MHZ ? "40" : "",
+                  status->flag & RX_FLAG_80MHZ ? "80" : "",
+                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
+                  status->rate_idx,
+                  status->vht_nss,
+                  status->freq,
+                  status->band);
+
+       ieee80211_rx(ar->hw, info->skb);
+}
+
+struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+                                    const u8 *addr)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       list_for_each_entry(peer, &ar->peers, list) {
+               if (peer->vdev_id != vdev_id)
+                       continue;
+               if (memcmp(peer->addr, addr, ETH_ALEN))
+                       continue;
+
+               return peer;
+       }
+
+       return NULL;
+}
+
+static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar,
+                                                 int peer_id)
+{
+       struct ath10k_peer *peer;
+
+       lockdep_assert_held(&ar->data_lock);
+
+       list_for_each_entry(peer, &ar->peers, list)
+               if (test_bit(peer_id, peer->peer_ids))
+                       return peer;
+
+       return NULL;
+}
+
+static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id,
+                                      const u8 *addr, bool expect_mapped)
+{
+       int ret;
+
+       ret = wait_event_timeout(ar->peer_mapping_wq, ({
+                       bool mapped;
+
+                       spin_lock_bh(&ar->data_lock);
+                       mapped = !!ath10k_peer_find(ar, vdev_id, addr);
+                       spin_unlock_bh(&ar->data_lock);
+
+                       mapped == expect_mapped;
+               }), 3*HZ);
+
+       if (ret <= 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr)
+{
+       return ath10k_wait_for_peer_common(ar, vdev_id, addr, true);
+}
+
+int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, const u8 *addr)
+{
+       return ath10k_wait_for_peer_common(ar, vdev_id, addr, false);
+}
+
+void ath10k_peer_map_event(struct ath10k_htt *htt,
+                          struct htt_peer_map_event *ev)
+{
+       struct ath10k *ar = htt->ar;
+       struct ath10k_peer *peer;
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr);
+       if (!peer) {
+               peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
+               if (!peer)
+                       goto exit;
+
+               peer->vdev_id = ev->vdev_id;
+               memcpy(peer->addr, ev->addr, ETH_ALEN);
+               list_add(&peer->list, &ar->peers);
+               wake_up(&ar->peer_mapping_wq);
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
+                  ev->vdev_id, ev->addr, ev->peer_id);
+
+       set_bit(ev->peer_id, peer->peer_ids);
+exit:
+       spin_unlock_bh(&ar->data_lock);
+}
+
+void ath10k_peer_unmap_event(struct ath10k_htt *htt,
+                            struct htt_peer_unmap_event *ev)
+{
+       struct ath10k *ar = htt->ar;
+       struct ath10k_peer *peer;
+
+       spin_lock_bh(&ar->data_lock);
+       peer = ath10k_peer_find_by_id(ar, ev->peer_id);
+       if (!peer) {
+               ath10k_warn("unknown peer id %d\n", ev->peer_id);
+               goto exit;
+       }
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
+                  peer->vdev_id, peer->addr, ev->peer_id);
+
+       clear_bit(ev->peer_id, peer->peer_ids);
+
+       if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
+               list_del(&peer->list);
+               kfree(peer);
+               wake_up(&ar->peer_mapping_wq);
+       }
+
+exit:
+       spin_unlock_bh(&ar->data_lock);
+}
diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h
new file mode 100644 (file)
index 0000000..e78632a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _TXRX_H_
+#define _TXRX_H_
+
+#include "htt.h"
+
+void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc);
+void ath10k_txrx_tx_completed(struct ath10k_htt *htt,
+                             const struct htt_tx_done *tx_done);
+void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info);
+
+struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
+                                    const u8 *addr);
+int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id,
+                                const u8 *addr);
+int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id,
+                                const u8 *addr);
+
+void ath10k_peer_map_event(struct ath10k_htt *htt,
+                          struct htt_peer_map_event *ev);
+void ath10k_peer_unmap_event(struct ath10k_htt *htt,
+                            struct htt_peer_unmap_event *ev);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
new file mode 100644 (file)
index 0000000..7d4b798
--- /dev/null
@@ -0,0 +1,2081 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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/skbuff.h>
+
+#include "core.h"
+#include "htc.h"
+#include "debug.h"
+#include "wmi.h"
+#include "mac.h"
+
+void ath10k_wmi_flush_tx(struct ath10k *ar)
+{
+       int ret;
+
+       ret = wait_event_timeout(ar->wmi.wq,
+                                atomic_read(&ar->wmi.pending_tx_count) == 0,
+                                5*HZ);
+       if (atomic_read(&ar->wmi.pending_tx_count) == 0)
+               return;
+
+       if (ret == 0)
+               ret = -ETIMEDOUT;
+
+       if (ret < 0)
+               ath10k_warn("wmi flush failed (%d)\n", ret);
+}
+
+int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
+{
+       int ret;
+       ret = wait_for_completion_timeout(&ar->wmi.service_ready,
+                                         WMI_SERVICE_READY_TIMEOUT_HZ);
+       return ret;
+}
+
+int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar)
+{
+       int ret;
+       ret = wait_for_completion_timeout(&ar->wmi.unified_ready,
+                                         WMI_UNIFIED_READY_TIMEOUT_HZ);
+       return ret;
+}
+
+static struct sk_buff *ath10k_wmi_alloc_skb(u32 len)
+{
+       struct sk_buff *skb;
+       u32 round_len = roundup(len, 4);
+
+       skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len);
+       if (!skb)
+               return NULL;
+
+       skb_reserve(skb, WMI_SKB_HEADROOM);
+       if (!IS_ALIGNED((unsigned long)skb->data, 4))
+               ath10k_warn("Unaligned WMI skb\n");
+
+       skb_put(skb, round_len);
+       memset(skb->data, 0, round_len);
+
+       return skb;
+}
+
+static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+       dev_kfree_skb(skb);
+
+       if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0)
+               wake_up(&ar->wmi.wq);
+}
+
+/* WMI command API */
+static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
+                              enum wmi_cmd_id cmd_id)
+{
+       struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+       struct wmi_cmd_hdr *cmd_hdr;
+       int status;
+       u32 cmd = 0;
+
+       if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return -ENOMEM;
+
+       cmd |= SM(cmd_id, WMI_CMD_HDR_CMD_ID);
+
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       cmd_hdr->cmd_id = __cpu_to_le32(cmd);
+
+       if (atomic_add_return(1, &ar->wmi.pending_tx_count) >
+           WMI_MAX_PENDING_TX_COUNT) {
+               /* avoid using up memory when FW hangs */
+               atomic_dec(&ar->wmi.pending_tx_count);
+               return -EBUSY;
+       }
+
+       memset(skb_cb, 0, sizeof(*skb_cb));
+
+       trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
+
+       status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
+       if (status) {
+               dev_kfree_skb_any(skb);
+               atomic_dec(&ar->wmi.pending_tx_count);
+               return status;
+       }
+
+       return 0;
+}
+
+static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
+       enum wmi_scan_event_type event_type;
+       enum wmi_scan_completion_reason reason;
+       u32 freq;
+       u32 req_id;
+       u32 scan_id;
+       u32 vdev_id;
+
+       event_type = __le32_to_cpu(event->event_type);
+       reason     = __le32_to_cpu(event->reason);
+       freq       = __le32_to_cpu(event->channel_freq);
+       req_id     = __le32_to_cpu(event->scan_req_id);
+       scan_id    = __le32_to_cpu(event->scan_id);
+       vdev_id    = __le32_to_cpu(event->vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n");
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "scan event type %d reason %d freq %d req_id %d "
+                  "scan_id %d vdev_id %d\n",
+                  event_type, reason, freq, req_id, scan_id, vdev_id);
+
+       spin_lock_bh(&ar->data_lock);
+
+       switch (event_type) {
+       case WMI_SCAN_EVENT_STARTED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n");
+               if (ar->scan.in_progress && ar->scan.is_roc)
+                       ieee80211_ready_on_channel(ar->hw);
+
+               complete(&ar->scan.started);
+               break;
+       case WMI_SCAN_EVENT_COMPLETED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n");
+               switch (reason) {
+               case WMI_SCAN_REASON_COMPLETED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n");
+                       break;
+               case WMI_SCAN_REASON_CANCELLED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n");
+                       break;
+               case WMI_SCAN_REASON_PREEMPTED:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n");
+                       break;
+               case WMI_SCAN_REASON_TIMEDOUT:
+                       ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n");
+                       break;
+               default:
+                       break;
+               }
+
+               ar->scan_channel = NULL;
+               if (!ar->scan.in_progress) {
+                       ath10k_warn("no scan requested, ignoring\n");
+                       break;
+               }
+
+               if (ar->scan.is_roc) {
+                       ath10k_offchan_tx_purge(ar);
+
+                       if (!ar->scan.aborting)
+                               ieee80211_remain_on_channel_expired(ar->hw);
+               } else {
+                       ieee80211_scan_completed(ar->hw, ar->scan.aborting);
+               }
+
+               del_timer(&ar->scan.timeout);
+               complete_all(&ar->scan.completed);
+               ar->scan.in_progress = false;
+               break;
+       case WMI_SCAN_EVENT_BSS_CHANNEL:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n");
+               ar->scan_channel = NULL;
+               break;
+       case WMI_SCAN_EVENT_FOREIGN_CHANNEL:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n");
+               ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq);
+               if (ar->scan.in_progress && ar->scan.is_roc &&
+                   ar->scan.roc_freq == freq) {
+                       complete(&ar->scan.on_channel);
+               }
+               break;
+       case WMI_SCAN_EVENT_DEQUEUED:
+               ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n");
+               break;
+       case WMI_SCAN_EVENT_PREEMPTED:
+               ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n");
+               break;
+       case WMI_SCAN_EVENT_START_FAILED:
+               ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n");
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock_bh(&ar->data_lock);
+       return 0;
+}
+
+static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode)
+{
+       enum ieee80211_band band;
+
+       switch (phy_mode) {
+       case MODE_11A:
+       case MODE_11NA_HT20:
+       case MODE_11NA_HT40:
+       case MODE_11AC_VHT20:
+       case MODE_11AC_VHT40:
+       case MODE_11AC_VHT80:
+               band = IEEE80211_BAND_5GHZ;
+               break;
+       case MODE_11G:
+       case MODE_11B:
+       case MODE_11GONLY:
+       case MODE_11NG_HT20:
+       case MODE_11NG_HT40:
+       case MODE_11AC_VHT20_2G:
+       case MODE_11AC_VHT40_2G:
+       case MODE_11AC_VHT80_2G:
+       default:
+               band = IEEE80211_BAND_2GHZ;
+       }
+
+       return band;
+}
+
+static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band)
+{
+       u8 rate_idx = 0;
+
+       /* rate in Kbps */
+       switch (rate) {
+       case 1000:
+               rate_idx = 0;
+               break;
+       case 2000:
+               rate_idx = 1;
+               break;
+       case 5500:
+               rate_idx = 2;
+               break;
+       case 11000:
+               rate_idx = 3;
+               break;
+       case 6000:
+               rate_idx = 4;
+               break;
+       case 9000:
+               rate_idx = 5;
+               break;
+       case 12000:
+               rate_idx = 6;
+               break;
+       case 18000:
+               rate_idx = 7;
+               break;
+       case 24000:
+               rate_idx = 8;
+               break;
+       case 36000:
+               rate_idx = 9;
+               break;
+       case 48000:
+               rate_idx = 10;
+               break;
+       case 54000:
+               rate_idx = 11;
+               break;
+       default:
+               break;
+       }
+
+       if (band == IEEE80211_BAND_5GHZ) {
+               if (rate_idx > 3)
+                       /* Omit CCK rates */
+                       rate_idx -= 4;
+               else
+                       rate_idx = 0;
+       }
+
+       return rate_idx;
+}
+
+static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_hdr *hdr;
+       u32 rx_status;
+       u32 channel;
+       u32 phy_mode;
+       u32 snr;
+       u32 rate;
+       u32 buf_len;
+       u16 fc;
+
+       channel   = __le32_to_cpu(event->hdr.channel);
+       buf_len   = __le32_to_cpu(event->hdr.buf_len);
+       rx_status = __le32_to_cpu(event->hdr.status);
+       snr       = __le32_to_cpu(event->hdr.snr);
+       phy_mode  = __le32_to_cpu(event->hdr.phy_mode);
+       rate      = __le32_to_cpu(event->hdr.rate);
+
+       memset(status, 0, sizeof(*status));
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx status %08x\n", rx_status);
+
+       if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       if (rx_status & WMI_RX_STATUS_ERR_CRC)
+               status->flag |= RX_FLAG_FAILED_FCS_CRC;
+       if (rx_status & WMI_RX_STATUS_ERR_MIC)
+               status->flag |= RX_FLAG_MMIC_ERROR;
+
+       status->band = phy_mode_to_band(phy_mode);
+       status->freq = ieee80211_channel_to_frequency(channel, status->band);
+       status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
+       status->rate_idx = get_rate_idx(rate, status->band);
+
+       skb_pull(skb, sizeof(event->hdr));
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       fc = le16_to_cpu(hdr->frame_control);
+
+       if (fc & IEEE80211_FCTL_PROTECTED) {
+               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
+                               RX_FLAG_MMIC_STRIPPED;
+               hdr->frame_control = __cpu_to_le16(fc &
+                                       ~IEEE80211_FCTL_PROTECTED);
+       }
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx skb %p len %d ftype %02x stype %02x\n",
+                  skb, skb->len,
+                  fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE);
+
+       ath10k_dbg(ATH10K_DBG_MGMT,
+                  "event mgmt rx freq %d band %d snr %d, rate_idx %d\n",
+                  status->freq, status->band, status->signal,
+                  status->rate_idx);
+
+       /*
+        * packets from HTC come aligned to 4byte boundaries
+        * because they can originally come in along with a trailer
+        */
+       skb_trim(skb, buf_len);
+
+       ieee80211_rx(ar->hw, skb);
+       return 0;
+}
+
+static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+}
+
+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)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_update_stats(struct ath10k *ar,
+                                         struct sk_buff *skb)
+{
+       struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
+
+       ath10k_debug_read_target_stats(ar, ev);
+}
+
+static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       struct wmi_vdev_start_response_event *ev;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
+
+       ev = (struct wmi_vdev_start_response_event *)skb->data;
+
+       if (WARN_ON(__le32_to_cpu(ev->status)))
+               return;
+
+       complete(&ar->vdev_setup_done);
+}
+
+static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
+                                         struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
+       complete(&ar->vdev_setup_done);
+}
+
+static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n");
+}
+
+/*
+ * FIXME
+ *
+ * We don't report to mac80211 sleep state of connected
+ * stations. Due to this mac80211 can't fill in TIM IE
+ * correctly.
+ *
+ * I know of no way of getting nullfunc frames that contain
+ * sleep transition from connected stations - these do not
+ * seem to be sent from the target to the host. There also
+ * doesn't seem to be a dedicated event for that. So the
+ * only way left to do this would be to read tim_bitmap
+ * during SWBA.
+ *
+ * We could probably try using tim_bitmap from SWBA to tell
+ * mac80211 which stations are asleep and which are not. The
+ * problem here is calling mac80211 functions so many times
+ * could take too long and make us miss the time to submit
+ * the beacon to the target.
+ *
+ * So as a workaround we try to extend the TIM IE if there
+ * is unicast buffered for stations with aid > 7 and fill it
+ * in ourselves.
+ */
+static void ath10k_wmi_update_tim(struct ath10k *ar,
+                                 struct ath10k_vif *arvif,
+                                 struct sk_buff *bcn,
+                                 struct wmi_bcn_info *bcn_info)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
+       struct ieee80211_tim_ie *tim;
+       u8 *ies, *ie;
+       u8 ie_len, pvm_len;
+
+       /* if next SWBA has no tim_changed the tim_bitmap is garbage.
+        * we must copy the bitmap upon change and reuse it later */
+       if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) {
+               int i;
+
+               BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
+                            sizeof(bcn_info->tim_info.tim_bitmap));
+
+               for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
+                       __le32 t = bcn_info->tim_info.tim_bitmap[i / 4];
+                       u32 v = __le32_to_cpu(t);
+                       arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
+               }
+
+               /* FW reports either length 0 or 16
+                * so we calculate this on our own */
+               arvif->u.ap.tim_len = 0;
+               for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++)
+                       if (arvif->u.ap.tim_bitmap[i])
+                               arvif->u.ap.tim_len = i;
+
+               arvif->u.ap.tim_len++;
+       }
+
+       ies = bcn->data;
+       ies += ieee80211_hdrlen(hdr->frame_control);
+       ies += 12; /* fixed parameters */
+
+       ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
+                                   (u8 *)skb_tail_pointer(bcn) - ies);
+       if (!ie) {
+               /* highly unlikely for mac80211 */
+               ath10k_warn("no tim ie found;\n");
+               return;
+       }
+
+       tim = (void *)ie + 2;
+       ie_len = ie[1];
+       pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */
+
+       if (pvm_len < arvif->u.ap.tim_len) {
+               int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len;
+               int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len);
+               void *next_ie = ie + 2 + ie_len;
+
+               if (skb_put(bcn, expand_size)) {
+                       memmove(next_ie + expand_size, next_ie, move_size);
+
+                       ie[1] += expand_size;
+                       ie_len += expand_size;
+                       pvm_len += expand_size;
+               } else {
+                       ath10k_warn("tim expansion failed\n");
+               }
+       }
+
+       if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) {
+               ath10k_warn("tim pvm length is too great (%d)\n", pvm_len);
+               return;
+       }
+
+       tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
+       memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
+                  tim->dtim_count, tim->dtim_period,
+                  tim->bitmap_ctrl, pvm_len);
+}
+
+static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len,
+                                  struct wmi_p2p_noa_info *noa)
+{
+       struct ieee80211_p2p_noa_attr *noa_attr;
+       u8  ctwindow_oppps = noa->ctwindow_oppps;
+       u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET;
+       bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT);
+       __le16 *noa_attr_len;
+       u16 attr_len;
+       u8 noa_descriptors = noa->num_descriptors;
+       int i;
+
+       /* P2P IE */
+       data[0] = WLAN_EID_VENDOR_SPECIFIC;
+       data[1] = len - 2;
+       data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
+       data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
+       data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
+       data[5] = WLAN_OUI_TYPE_WFA_P2P;
+
+       /* NOA ATTR */
+       data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
+       noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
+       noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
+
+       noa_attr->index = noa->index;
+       noa_attr->oppps_ctwindow = ctwindow;
+       if (oppps)
+               noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
+
+       for (i = 0; i < noa_descriptors; i++) {
+               noa_attr->desc[i].count =
+                       __le32_to_cpu(noa->descriptors[i].type_count);
+               noa_attr->desc[i].duration = noa->descriptors[i].duration;
+               noa_attr->desc[i].interval = noa->descriptors[i].interval;
+               noa_attr->desc[i].start_time = noa->descriptors[i].start_time;
+       }
+
+       attr_len = 2; /* index + oppps_ctwindow */
+       attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+       *noa_attr_len = __cpu_to_le16(attr_len);
+}
+
+static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa)
+{
+       u32 len = 0;
+       u8 noa_descriptors = noa->num_descriptors;
+       u8 opp_ps_info = noa->ctwindow_oppps;
+       bool opps_enabled = !!(opp_ps_info & WMI_P2P_OPPPS_ENABLE_BIT);
+
+
+       if (!noa_descriptors && !opps_enabled)
+               return len;
+
+       len += 1 + 1 + 4; /* EID + len + OUI */
+       len += 1 + 2; /* noa attr  + attr len */
+       len += 1 + 1; /* index + oppps_ctwindow */
+       len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
+
+       return len;
+}
+
+static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
+                                 struct sk_buff *bcn,
+                                 struct wmi_bcn_info *bcn_info)
+{
+       struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info;
+       u8 *new_data, *old_data = arvif->u.ap.noa_data;
+       u32 new_len;
+
+       if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+               return;
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed);
+       if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) {
+               new_len = ath10k_p2p_calc_noa_ie_len(noa);
+               if (!new_len)
+                       goto cleanup;
+
+               new_data = kmalloc(new_len, GFP_ATOMIC);
+               if (!new_data)
+                       goto cleanup;
+
+               ath10k_p2p_fill_noa_ie(new_data, new_len, noa);
+
+               spin_lock_bh(&ar->data_lock);
+               arvif->u.ap.noa_data = new_data;
+               arvif->u.ap.noa_len = new_len;
+               spin_unlock_bh(&ar->data_lock);
+               kfree(old_data);
+       }
+
+       if (arvif->u.ap.noa_data)
+               if (!pskb_expand_head(bcn, 0, arvif->u.ap.noa_len, GFP_ATOMIC))
+                       memcpy(skb_put(bcn, arvif->u.ap.noa_len),
+                              arvif->u.ap.noa_data,
+                              arvif->u.ap.noa_len);
+       return;
+
+cleanup:
+       spin_lock_bh(&ar->data_lock);
+       arvif->u.ap.noa_data = NULL;
+       arvif->u.ap.noa_len = 0;
+       spin_unlock_bh(&ar->data_lock);
+       kfree(old_data);
+}
+
+
+static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_host_swba_event *ev;
+       u32 map;
+       int i = -1;
+       struct wmi_bcn_info *bcn_info;
+       struct ath10k_vif *arvif;
+       struct wmi_bcn_tx_arg arg;
+       struct sk_buff *bcn;
+       int vdev_id = 0;
+       int ret;
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
+
+       ev = (struct wmi_host_swba_event *)skb->data;
+       map = __le32_to_cpu(ev->vdev_map);
+
+       ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n"
+                  "-vdev map 0x%x\n",
+                  ev->vdev_map);
+
+       for (; map; map >>= 1, vdev_id++) {
+               if (!(map & 0x1))
+                       continue;
+
+               i++;
+
+               if (i >= WMI_MAX_AP_VDEV) {
+                       ath10k_warn("swba has corrupted vdev map\n");
+                       break;
+               }
+
+               bcn_info = &ev->bcn_info[i];
+
+               ath10k_dbg(ATH10K_DBG_MGMT,
+                          "-bcn_info[%d]:\n"
+                          "--tim_len %d\n"
+                          "--tim_mcast %d\n"
+                          "--tim_changed %d\n"
+                          "--tim_num_ps_pending %d\n"
+                          "--tim_bitmap 0x%08x%08x%08x%08x\n",
+                          i,
+                          __le32_to_cpu(bcn_info->tim_info.tim_len),
+                          __le32_to_cpu(bcn_info->tim_info.tim_mcast),
+                          __le32_to_cpu(bcn_info->tim_info.tim_changed),
+                          __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]),
+                          __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0]));
+
+               arvif = ath10k_get_arvif(ar, vdev_id);
+               if (arvif == NULL) {
+                       ath10k_warn("no vif for vdev_id %d found\n", vdev_id);
+                       continue;
+               }
+
+               bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
+               if (!bcn) {
+                       ath10k_warn("could not get mac80211 beacon\n");
+                       continue;
+               }
+
+               ath10k_tx_h_seq_no(bcn);
+               ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
+               ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
+
+               arg.vdev_id = arvif->vdev_id;
+               arg.tx_rate = 0;
+               arg.tx_power = 0;
+               arg.bcn = bcn->data;
+               arg.bcn_len = bcn->len;
+
+               ret = ath10k_wmi_beacon_send(ar, &arg);
+               if (ret)
+                       ath10k_warn("could not send beacon (%d)\n", ret);
+
+               dev_kfree_skb_any(bcn);
+       }
+}
+
+static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
+                                              struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n");
+}
+
+static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
+}
+
+static void ath10k_wmi_event_profile_match(struct ath10k *ar,
+                                   struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
+}
+
+static void ath10k_wmi_event_debug_print(struct ath10k *ar,
+                                 struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar,
+                                              struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
+}
+
+static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
+}
+
+static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
+}
+
+static void ath10k_wmi_event_dcs_interference(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar,
+                                            struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar,
+                                          struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
+}
+
+static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
+                                        struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
+}
+
+static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
+}
+
+static void ath10k_wmi_event_delba_complete(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_addba_complete(struct ath10k *ar,
+                                           struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
+                                               struct sk_buff *skb)
+{
+       ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
+}
+
+static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
+                                             struct sk_buff *skb)
+{
+       struct wmi_service_ready_event *ev = (void *)skb->data;
+
+       if (skb->len < sizeof(*ev)) {
+               ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
+                           skb->len, sizeof(*ev));
+               return;
+       }
+
+       ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
+       ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
+       ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
+       ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+       ar->fw_version_major =
+               (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
+       ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+       ar->fw_version_release =
+               (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
+       ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
+       ar->phy_capability = __le32_to_cpu(ev->phy_capability);
+
+       ar->ath_common.regulatory.current_rd =
+               __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
+
+       ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap,
+                                     sizeof(ev->wmi_service_bitmap));
+
+       if (strlen(ar->hw->wiphy->fw_version) == 0) {
+               snprintf(ar->hw->wiphy->fw_version,
+                        sizeof(ar->hw->wiphy->fw_version),
+                        "%u.%u.%u.%u",
+                        ar->fw_version_major,
+                        ar->fw_version_minor,
+                        ar->fw_version_release,
+                        ar->fw_version_build);
+       }
+
+       /* FIXME: it probably should be better to support this */
+       if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
+               ath10k_warn("target requested %d memory chunks; ignoring\n",
+                           __le32_to_cpu(ev->num_mem_reqs));
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->sw_version_1),
+                  __le32_to_cpu(ev->abi_version),
+                  __le32_to_cpu(ev->phy_capability),
+                  __le32_to_cpu(ev->ht_cap_info),
+                  __le32_to_cpu(ev->vht_cap_info),
+                  __le32_to_cpu(ev->vht_supp_mcs),
+                  __le32_to_cpu(ev->sys_cap_info),
+                  __le32_to_cpu(ev->num_mem_reqs));
+
+       complete(&ar->wmi.service_ready);
+}
+
+static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
+
+       if (WARN_ON(skb->len < sizeof(*ev)))
+               return -EINVAL;
+
+       memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+                  __le32_to_cpu(ev->sw_version),
+                  __le32_to_cpu(ev->abi_version),
+                  ev->mac_addr.addr,
+                  __le32_to_cpu(ev->status));
+
+       complete(&ar->wmi.unified_ready);
+       return 0;
+}
+
+static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd_hdr;
+       enum wmi_event_id id;
+       u16 len;
+
+       cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+       if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
+               return;
+
+       len = skb->len;
+
+       trace_ath10k_wmi_event(id, skb->data, skb->len);
+
+       switch (id) {
+       case WMI_MGMT_RX_EVENTID:
+               ath10k_wmi_event_mgmt_rx(ar, skb);
+               /* mgmt_rx() owns the skb now! */
+               return;
+       case WMI_SCAN_EVENTID:
+               ath10k_wmi_event_scan(ar, skb);
+               break;
+       case WMI_CHAN_INFO_EVENTID:
+               ath10k_wmi_event_chan_info(ar, skb);
+               break;
+       case WMI_ECHO_EVENTID:
+               ath10k_wmi_event_echo(ar, skb);
+               break;
+       case WMI_DEBUG_MESG_EVENTID:
+               ath10k_wmi_event_debug_mesg(ar, skb);
+               break;
+       case WMI_UPDATE_STATS_EVENTID:
+               ath10k_wmi_event_update_stats(ar, skb);
+               break;
+       case WMI_VDEV_START_RESP_EVENTID:
+               ath10k_wmi_event_vdev_start_resp(ar, skb);
+               break;
+       case WMI_VDEV_STOPPED_EVENTID:
+               ath10k_wmi_event_vdev_stopped(ar, skb);
+               break;
+       case WMI_PEER_STA_KICKOUT_EVENTID:
+               ath10k_wmi_event_peer_sta_kickout(ar, skb);
+               break;
+       case WMI_HOST_SWBA_EVENTID:
+               ath10k_wmi_event_host_swba(ar, skb);
+               break;
+       case WMI_TBTTOFFSET_UPDATE_EVENTID:
+               ath10k_wmi_event_tbttoffset_update(ar, skb);
+               break;
+       case WMI_PHYERR_EVENTID:
+               ath10k_wmi_event_phyerr(ar, skb);
+               break;
+       case WMI_ROAM_EVENTID:
+               ath10k_wmi_event_roam(ar, skb);
+               break;
+       case WMI_PROFILE_MATCH:
+               ath10k_wmi_event_profile_match(ar, skb);
+               break;
+       case WMI_DEBUG_PRINT_EVENTID:
+               ath10k_wmi_event_debug_print(ar, skb);
+               break;
+       case WMI_PDEV_QVIT_EVENTID:
+               ath10k_wmi_event_pdev_qvit(ar, skb);
+               break;
+       case WMI_WLAN_PROFILE_DATA_EVENTID:
+               ath10k_wmi_event_wlan_profile_data(ar, skb);
+               break;
+       case WMI_RTT_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_measurement_report(ar, skb);
+               break;
+       case WMI_TSF_MEASUREMENT_REPORT_EVENTID:
+               ath10k_wmi_event_tsf_measurement_report(ar, skb);
+               break;
+       case WMI_RTT_ERROR_REPORT_EVENTID:
+               ath10k_wmi_event_rtt_error_report(ar, skb);
+               break;
+       case WMI_WOW_WAKEUP_HOST_EVENTID:
+               ath10k_wmi_event_wow_wakeup_host(ar, skb);
+               break;
+       case WMI_DCS_INTERFERENCE_EVENTID:
+               ath10k_wmi_event_dcs_interference(ar, skb);
+               break;
+       case WMI_PDEV_TPC_CONFIG_EVENTID:
+               ath10k_wmi_event_pdev_tpc_config(ar, skb);
+               break;
+       case WMI_PDEV_FTM_INTG_EVENTID:
+               ath10k_wmi_event_pdev_ftm_intg(ar, skb);
+               break;
+       case WMI_GTK_OFFLOAD_STATUS_EVENTID:
+               ath10k_wmi_event_gtk_offload_status(ar, skb);
+               break;
+       case WMI_GTK_REKEY_FAIL_EVENTID:
+               ath10k_wmi_event_gtk_rekey_fail(ar, skb);
+               break;
+       case WMI_TX_DELBA_COMPLETE_EVENTID:
+               ath10k_wmi_event_delba_complete(ar, skb);
+               break;
+       case WMI_TX_ADDBA_COMPLETE_EVENTID:
+               ath10k_wmi_event_addba_complete(ar, skb);
+               break;
+       case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID:
+               ath10k_wmi_event_vdev_install_key_complete(ar, skb);
+               break;
+       case WMI_SERVICE_READY_EVENTID:
+               ath10k_wmi_service_ready_event_rx(ar, skb);
+               break;
+       case WMI_READY_EVENTID:
+               ath10k_wmi_ready_event_rx(ar, skb);
+               break;
+       default:
+               ath10k_warn("Unknown eventid: %d\n", id);
+               break;
+       }
+
+       dev_kfree_skb(skb);
+}
+
+static void ath10k_wmi_event_work(struct work_struct *work)
+{
+       struct ath10k *ar = container_of(work, struct ath10k,
+                                        wmi.wmi_event_work);
+       struct sk_buff *skb;
+
+       for (;;) {
+               skb = skb_dequeue(&ar->wmi.wmi_event_list);
+               if (!skb)
+                       break;
+
+               ath10k_wmi_event_process(ar, skb);
+       }
+}
+
+static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
+       enum wmi_event_id event_id;
+
+       event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
+
+       /* some events require to be handled ASAP
+        * thus can't be defered to a worker thread */
+       switch (event_id) {
+       case WMI_HOST_SWBA_EVENTID:
+       case WMI_MGMT_RX_EVENTID:
+               ath10k_wmi_event_process(ar, skb);
+               return;
+       default:
+               break;
+       }
+
+       skb_queue_tail(&ar->wmi.wmi_event_list, skb);
+       queue_work(ar->workqueue, &ar->wmi.wmi_event_work);
+}
+
+/* WMI Initialization functions */
+int ath10k_wmi_attach(struct ath10k *ar)
+{
+       init_completion(&ar->wmi.service_ready);
+       init_completion(&ar->wmi.unified_ready);
+       init_waitqueue_head(&ar->wmi.wq);
+
+       skb_queue_head_init(&ar->wmi.wmi_event_list);
+       INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work);
+
+       return 0;
+}
+
+void ath10k_wmi_detach(struct ath10k *ar)
+{
+       /* HTC should've drained the packets already */
+       if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0))
+               ath10k_warn("there are still pending packets\n");
+
+       cancel_work_sync(&ar->wmi.wmi_event_work);
+       skb_queue_purge(&ar->wmi.wmi_event_list);
+}
+
+int ath10k_wmi_connect_htc_service(struct ath10k *ar)
+{
+       int status;
+       struct ath10k_htc_svc_conn_req conn_req;
+       struct ath10k_htc_svc_conn_resp conn_resp;
+
+       memset(&conn_req, 0, sizeof(conn_req));
+       memset(&conn_resp, 0, sizeof(conn_resp));
+
+       /* these fields are the same for all service endpoints */
+       conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete;
+       conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx;
+
+       /* connect to control service */
+       conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
+
+       status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
+       if (status) {
+               ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
+                           status);
+               return status;
+       }
+
+       ar->wmi.eid = conn_resp.eid;
+       return 0;
+}
+
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g)
+{
+       struct wmi_pdev_set_regdomain_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data;
+       cmd->reg_domain = __cpu_to_le32(rd);
+       cmd->reg_domain_2G = __cpu_to_le32(rd2g);
+       cmd->reg_domain_5G = __cpu_to_le32(rd5g);
+       cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g);
+       cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
+                  rd, rd2g, rd5g, ctl2g, ctl5g);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID);
+}
+
+int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+                               const struct wmi_channel_arg *arg)
+{
+       struct wmi_set_channel_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->passive)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_set_channel_cmd *)skb->data;
+       cmd->chan.mhz               = __cpu_to_le32(arg->freq);
+       cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
+       cmd->chan.mode              = arg->mode;
+       cmd->chan.min_power         = arg->min_power;
+       cmd->chan.max_power         = arg->max_power;
+       cmd->chan.reg_power         = arg->max_reg_power;
+       cmd->chan.reg_classid       = arg->reg_class_id;
+       cmd->chan.antenna_max       = arg->max_antenna_gain;
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi set channel mode %d freq %d\n",
+                  arg->mode, arg->freq);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID);
+}
+
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
+{
+       struct wmi_pdev_suspend_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
+       cmd->suspend_opt = WMI_PDEV_SUSPEND;
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID);
+}
+
+int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
+{
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(0);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID);
+}
+
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
+                             u32 value)
+{
+       struct wmi_pdev_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_param_cmd *)skb->data;
+       cmd->param_id    = __cpu_to_le32(id);
+       cmd->param_value = __cpu_to_le32(value);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
+                  id, value);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_cmd_init(struct ath10k *ar)
+{
+       struct wmi_init_cmd *cmd;
+       struct sk_buff *buf;
+       struct wmi_resource_config config = {};
+       u32 val;
+
+       config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
+       config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+       config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
+
+       config.num_offload_reorder_bufs =
+               __cpu_to_le32(TARGET_NUM_OFFLOAD_REORDER_BUFS);
+
+       config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS);
+       config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS);
+       config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT);
+       config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK);
+       config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK);
+       config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI);
+       config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI);
+       config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE);
+
+       config.scan_max_pending_reqs =
+               __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS);
+
+       config.bmiss_offload_max_vdev =
+               __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_vdev =
+               __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV);
+
+       config.roam_offload_max_ap_profiles =
+               __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES);
+
+       config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS);
+       config.num_mcast_table_elems =
+               __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS);
+
+       config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE);
+       config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE);
+       config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES);
+       config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE);
+       config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM);
+
+       val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK;
+       config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val);
+
+       config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG);
+
+       config.gtk_offload_max_vdev =
+               __cpu_to_le32(TARGET_GTK_OFFLOAD_MAX_VDEV);
+
+       config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC);
+       config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES);
+
+       buf = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!buf)
+               return -ENOMEM;
+
+       cmd = (struct wmi_init_cmd *)buf->data;
+       cmd->num_host_mem_chunks = 0;
+       memcpy(&cmd->resource_config, &config, sizeof(config));
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n");
+       return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID);
+}
+
+static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg)
+{
+       int len;
+
+       len = sizeof(struct wmi_start_scan_cmd);
+
+       if (arg->ie_len) {
+               if (!arg->ie)
+                       return -EINVAL;
+               if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_ie_data);
+               len += roundup(arg->ie_len, 4);
+       }
+
+       if (arg->n_channels) {
+               if (!arg->channels)
+                       return -EINVAL;
+               if (arg->n_channels > ARRAY_SIZE(arg->channels))
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_chan_list);
+               len += sizeof(__le32) * arg->n_channels;
+       }
+
+       if (arg->n_ssids) {
+               if (!arg->ssids)
+                       return -EINVAL;
+               if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_ssid_list);
+               len += sizeof(struct wmi_ssid) * arg->n_ssids;
+       }
+
+       if (arg->n_bssids) {
+               if (!arg->bssids)
+                       return -EINVAL;
+               if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
+                       return -EINVAL;
+
+               len += sizeof(struct wmi_bssid_list);
+               len += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+       }
+
+       return len;
+}
+
+int ath10k_wmi_start_scan(struct ath10k *ar,
+                         const struct wmi_start_scan_arg *arg)
+{
+       struct wmi_start_scan_cmd *cmd;
+       struct sk_buff *skb;
+       struct wmi_ie_data *ie;
+       struct wmi_chan_list *channels;
+       struct wmi_ssid_list *ssids;
+       struct wmi_bssid_list *bssids;
+       u32 scan_id;
+       u32 scan_req_id;
+       int off;
+       int len = 0;
+       int i;
+
+       len = ath10k_wmi_start_scan_calc_len(arg);
+       if (len < 0)
+               return len; /* len contains error code here */
+
+       skb = ath10k_wmi_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       scan_id  = WMI_HOST_SCAN_REQ_ID_PREFIX;
+       scan_id |= arg->scan_id;
+
+       scan_req_id  = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+       scan_req_id |= arg->scan_req_id;
+
+       cmd = (struct wmi_start_scan_cmd *)skb->data;
+       cmd->scan_id            = __cpu_to_le32(scan_id);
+       cmd->scan_req_id        = __cpu_to_le32(scan_req_id);
+       cmd->vdev_id            = __cpu_to_le32(arg->vdev_id);
+       cmd->scan_priority      = __cpu_to_le32(arg->scan_priority);
+       cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
+       cmd->dwell_time_active  = __cpu_to_le32(arg->dwell_time_active);
+       cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
+       cmd->min_rest_time      = __cpu_to_le32(arg->min_rest_time);
+       cmd->max_rest_time      = __cpu_to_le32(arg->max_rest_time);
+       cmd->repeat_probe_time  = __cpu_to_le32(arg->repeat_probe_time);
+       cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
+       cmd->idle_time          = __cpu_to_le32(arg->idle_time);
+       cmd->max_scan_time      = __cpu_to_le32(arg->max_scan_time);
+       cmd->probe_delay        = __cpu_to_le32(arg->probe_delay);
+       cmd->scan_ctrl_flags    = __cpu_to_le32(arg->scan_ctrl_flags);
+
+       /* TLV list starts after fields included in the struct */
+       off = sizeof(*cmd);
+
+       if (arg->n_channels) {
+               channels = (void *)skb->data + off;
+               channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG);
+               channels->num_chan = __cpu_to_le32(arg->n_channels);
+
+               for (i = 0; i < arg->n_channels; i++)
+                       channels->channel_list[i] =
+                               __cpu_to_le32(arg->channels[i]);
+
+               off += sizeof(*channels);
+               off += sizeof(__le32) * arg->n_channels;
+       }
+
+       if (arg->n_ssids) {
+               ssids = (void *)skb->data + off;
+               ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG);
+               ssids->num_ssids = __cpu_to_le32(arg->n_ssids);
+
+               for (i = 0; i < arg->n_ssids; i++) {
+                       ssids->ssids[i].ssid_len =
+                               __cpu_to_le32(arg->ssids[i].len);
+                       memcpy(&ssids->ssids[i].ssid,
+                              arg->ssids[i].ssid,
+                              arg->ssids[i].len);
+               }
+
+               off += sizeof(*ssids);
+               off += sizeof(struct wmi_ssid) * arg->n_ssids;
+       }
+
+       if (arg->n_bssids) {
+               bssids = (void *)skb->data + off;
+               bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG);
+               bssids->num_bssid = __cpu_to_le32(arg->n_bssids);
+
+               for (i = 0; i < arg->n_bssids; i++)
+                       memcpy(&bssids->bssid_list[i],
+                              arg->bssids[i].bssid,
+                              ETH_ALEN);
+
+               off += sizeof(*bssids);
+               off += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+       }
+
+       if (arg->ie_len) {
+               ie = (void *)skb->data + off;
+               ie->tag = __cpu_to_le32(WMI_IE_TAG);
+               ie->ie_len = __cpu_to_le32(arg->ie_len);
+               memcpy(ie->ie_data, arg->ie, arg->ie_len);
+
+               off += sizeof(*ie);
+               off += roundup(arg->ie_len, 4);
+       }
+
+       if (off != skb->len) {
+               dev_kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n");
+       return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID);
+}
+
+void ath10k_wmi_start_scan_init(struct ath10k *ar,
+                               struct wmi_start_scan_arg *arg)
+{
+       /* setup commonly used values */
+       arg->scan_req_id = 1;
+       arg->scan_priority = WMI_SCAN_PRIORITY_LOW;
+       arg->dwell_time_active = 50;
+       arg->dwell_time_passive = 150;
+       arg->min_rest_time = 50;
+       arg->max_rest_time = 500;
+       arg->repeat_probe_time = 0;
+       arg->probe_spacing_time = 0;
+       arg->idle_time = 0;
+       arg->max_scan_time = 5000;
+       arg->probe_delay = 5;
+       arg->notify_scan_events = WMI_SCAN_EVENT_STARTED
+               | WMI_SCAN_EVENT_COMPLETED
+               | WMI_SCAN_EVENT_BSS_CHANNEL
+               | WMI_SCAN_EVENT_FOREIGN_CHANNEL
+               | WMI_SCAN_EVENT_DEQUEUED;
+       arg->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES;
+       arg->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT;
+       arg->n_bssids = 1;
+       arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF";
+}
+
+int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
+{
+       struct wmi_stop_scan_cmd *cmd;
+       struct sk_buff *skb;
+       u32 scan_id;
+       u32 req_id;
+
+       if (arg->req_id > 0xFFF)
+               return -EINVAL;
+       if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       scan_id = arg->u.scan_id;
+       scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
+
+       req_id = arg->req_id;
+       req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
+
+       cmd = (struct wmi_stop_scan_cmd *)skb->data;
+       cmd->req_type    = __cpu_to_le32(arg->req_type);
+       cmd->vdev_id     = __cpu_to_le32(arg->u.vdev_id);
+       cmd->scan_id     = __cpu_to_le32(scan_id);
+       cmd->scan_req_id = __cpu_to_le32(req_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
+                  arg->req_id, arg->req_type, arg->u.scan_id);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID);
+}
+
+int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+                          enum wmi_vdev_type type,
+                          enum wmi_vdev_subtype subtype,
+                          const u8 macaddr[ETH_ALEN])
+{
+       struct wmi_vdev_create_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_create_cmd *)skb->data;
+       cmd->vdev_id      = __cpu_to_le32(vdev_id);
+       cmd->vdev_type    = __cpu_to_le32(type);
+       cmd->vdev_subtype = __cpu_to_le32(subtype);
+       memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
+                  vdev_id, type, subtype, macaddr);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID);
+}
+
+int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_delete_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_delete_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "WMI vdev delete id %d\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID);
+}
+
+static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
+                               const struct wmi_vdev_start_request_arg *arg,
+                               enum wmi_cmd_id cmd_id)
+{
+       struct wmi_vdev_start_request_cmd *cmd;
+       struct sk_buff *skb;
+       const char *cmdname;
+       u32 flags = 0;
+
+       if (cmd_id != WMI_VDEV_START_REQUEST_CMDID &&
+           cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID)
+               return -EINVAL;
+       if (WARN_ON(arg->ssid && arg->ssid_len == 0))
+               return -EINVAL;
+       if (WARN_ON(arg->hidden_ssid && !arg->ssid))
+               return -EINVAL;
+       if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
+               return -EINVAL;
+
+       if (cmd_id == WMI_VDEV_START_REQUEST_CMDID)
+               cmdname = "start";
+       else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID)
+               cmdname = "restart";
+       else
+               return -EINVAL; /* should not happen, we already check cmd_id */
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       if (arg->hidden_ssid)
+               flags |= WMI_VDEV_START_HIDDEN_SSID;
+       if (arg->pmf_enabled)
+               flags |= WMI_VDEV_START_PMF_ENABLED;
+
+       cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
+       cmd->vdev_id         = __cpu_to_le32(arg->vdev_id);
+       cmd->disable_hw_ack  = __cpu_to_le32(arg->disable_hw_ack);
+       cmd->beacon_interval = __cpu_to_le32(arg->bcn_intval);
+       cmd->dtim_period     = __cpu_to_le32(arg->dtim_period);
+       cmd->flags           = __cpu_to_le32(flags);
+       cmd->bcn_tx_rate     = __cpu_to_le32(arg->bcn_tx_rate);
+       cmd->bcn_tx_power    = __cpu_to_le32(arg->bcn_tx_power);
+
+       if (arg->ssid) {
+               cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len);
+               memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
+       }
+
+       cmd->chan.mhz = __cpu_to_le32(arg->channel.freq);
+
+       cmd->chan.band_center_freq1 =
+               __cpu_to_le32(arg->channel.band_center_freq1);
+
+       cmd->chan.mode = arg->channel.mode;
+       cmd->chan.min_power = arg->channel.min_power;
+       cmd->chan.max_power = arg->channel.max_power;
+       cmd->chan.reg_power = arg->channel.max_reg_power;
+       cmd->chan.reg_classid = arg->channel.reg_class_id;
+       cmd->chan.antenna_max = arg->channel.max_antenna_gain;
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
+                  "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
+                  arg->channel.mode, flags, arg->channel.max_power);
+
+       return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+int ath10k_wmi_vdev_start(struct ath10k *ar,
+                         const struct wmi_vdev_start_request_arg *arg)
+{
+       return ath10k_wmi_vdev_start_restart(ar, arg,
+                                            WMI_VDEV_START_REQUEST_CMDID);
+}
+
+int ath10k_wmi_vdev_restart(struct ath10k *ar,
+                    const struct wmi_vdev_start_request_arg *arg)
+{
+       return ath10k_wmi_vdev_start_restart(ar, arg,
+                                            WMI_VDEV_RESTART_REQUEST_CMDID);
+}
+
+int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_stop_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_stop_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID);
+}
+
+int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
+{
+       struct wmi_vdev_up_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_up_cmd *)skb->data;
+       cmd->vdev_id       = __cpu_to_le32(vdev_id);
+       cmd->vdev_assoc_id = __cpu_to_le32(aid);
+       memcpy(&cmd->vdev_bssid.addr, bssid, 6);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
+                  vdev_id, aid, bssid);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID);
+}
+
+int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
+{
+       struct wmi_vdev_down_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_down_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi mgmt vdev down id 0x%x\n", vdev_id);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID);
+}
+
+int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+                             enum wmi_vdev_param param_id, u32 param_value)
+{
+       struct wmi_vdev_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_set_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(param_value);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev id 0x%x set param %d value %d\n",
+                  vdev_id, param_id, param_value);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+                               const struct wmi_vdev_install_key_arg *arg)
+{
+       struct wmi_vdev_install_key_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
+               return -EINVAL;
+       if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
+       cmd->vdev_id       = __cpu_to_le32(arg->vdev_id);
+       cmd->key_idx       = __cpu_to_le32(arg->key_idx);
+       cmd->key_flags     = __cpu_to_le32(arg->key_flags);
+       cmd->key_cipher    = __cpu_to_le32(arg->key_cipher);
+       cmd->key_len       = __cpu_to_le32(arg->key_len);
+       cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len);
+       cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len);
+
+       if (arg->macaddr)
+               memcpy(cmd->peer_macaddr.addr, arg->macaddr, ETH_ALEN);
+       if (arg->key_data)
+               memcpy(cmd->key_data, arg->key_data, arg->key_len);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
+}
+
+int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+                          const u8 peer_addr[ETH_ALEN])
+{
+       struct wmi_peer_create_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_create_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer create vdev_id %d peer_addr %pM\n",
+                  vdev_id, peer_addr);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID);
+}
+
+int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+                          const u8 peer_addr[ETH_ALEN])
+{
+       struct wmi_peer_delete_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_delete_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer delete vdev_id %d peer_addr %pM\n",
+                  vdev_id, peer_addr);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID);
+}
+
+int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+                         const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
+{
+       struct wmi_peer_flush_tids_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_flush_tids_cmd *)skb->data;
+       cmd->vdev_id         = __cpu_to_le32(vdev_id);
+       cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap);
+       memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
+                  vdev_id, peer_addr, tid_bitmap);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID);
+}
+
+int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+                             const u8 *peer_addr, enum wmi_peer_param param_id,
+                             u32 param_value)
+{
+       struct wmi_peer_set_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_set_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(param_value);
+       memcpy(&cmd->peer_macaddr.addr, peer_addr, 6);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi vdev %d peer 0x%pM set param %d value %d\n",
+                  vdev_id, peer_addr, param_id, param_value);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID);
+}
+
+int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+                         enum wmi_sta_ps_mode psmode)
+{
+       struct wmi_sta_powersave_mode_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->sta_ps_mode = __cpu_to_le32(psmode);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi set powersave id 0x%x mode %d\n",
+                  vdev_id, psmode);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID);
+}
+
+int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_sta_powersave_param param_id,
+                               u32 value)
+{
+       struct wmi_sta_powersave_param_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_powersave_param_cmd *)skb->data;
+       cmd->vdev_id     = __cpu_to_le32(vdev_id);
+       cmd->param_id    = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(value);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi sta ps param vdev_id 0x%x param %d value %d\n",
+                  vdev_id, param_id, value);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID);
+}
+
+int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+                              enum wmi_ap_ps_peer_param param_id, u32 value)
+{
+       struct wmi_ap_ps_peer_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (!mac)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_ap_ps_peer_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(vdev_id);
+       cmd->param_id = __cpu_to_le32(param_id);
+       cmd->param_value = __cpu_to_le32(value);
+       memcpy(&cmd->peer_macaddr, mac, ETH_ALEN);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
+                  vdev_id, param_id, value, mac);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID);
+}
+
+int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+                             const struct wmi_scan_chan_list_arg *arg)
+{
+       struct wmi_scan_chan_list_cmd *cmd;
+       struct sk_buff *skb;
+       struct wmi_channel_arg *ch;
+       struct wmi_channel *ci;
+       int len;
+       int i;
+
+       len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel);
+
+       skb = ath10k_wmi_alloc_skb(len);
+       if (!skb)
+               return -EINVAL;
+
+       cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
+       cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
+
+       for (i = 0; i < arg->n_channels; i++) {
+               u32 flags = 0;
+
+               ch = &arg->channels[i];
+               ci = &cmd->chan_info[i];
+
+               if (ch->passive)
+                       flags |= WMI_CHAN_FLAG_PASSIVE;
+               if (ch->allow_ibss)
+                       flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
+               if (ch->allow_ht)
+                       flags |= WMI_CHAN_FLAG_ALLOW_HT;
+               if (ch->allow_vht)
+                       flags |= WMI_CHAN_FLAG_ALLOW_VHT;
+               if (ch->ht40plus)
+                       flags |= WMI_CHAN_FLAG_HT40_PLUS;
+
+               ci->mhz               = __cpu_to_le32(ch->freq);
+               ci->band_center_freq1 = __cpu_to_le32(ch->freq);
+               ci->band_center_freq2 = 0;
+               ci->min_power         = ch->min_power;
+               ci->max_power         = ch->max_power;
+               ci->reg_power         = ch->max_reg_power;
+               ci->antenna_max       = ch->max_antenna_gain;
+               ci->antenna_max       = 0;
+
+               /* mode & flags share storage */
+               ci->mode              = ch->mode;
+               ci->flags            |= __cpu_to_le32(flags);
+       }
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID);
+}
+
+int ath10k_wmi_peer_assoc(struct ath10k *ar,
+                         const struct wmi_peer_assoc_complete_arg *arg)
+{
+       struct wmi_peer_assoc_complete_cmd *cmd;
+       struct sk_buff *skb;
+
+       if (arg->peer_mpdu_density > 16)
+               return -EINVAL;
+       if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
+               return -EINVAL;
+       if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
+               return -EINVAL;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data;
+       cmd->vdev_id            = __cpu_to_le32(arg->vdev_id);
+       cmd->peer_new_assoc     = __cpu_to_le32(arg->peer_reassoc ? 0 : 1);
+       cmd->peer_associd       = __cpu_to_le32(arg->peer_aid);
+       cmd->peer_flags         = __cpu_to_le32(arg->peer_flags);
+       cmd->peer_caps          = __cpu_to_le32(arg->peer_caps);
+       cmd->peer_listen_intval = __cpu_to_le32(arg->peer_listen_intval);
+       cmd->peer_ht_caps       = __cpu_to_le32(arg->peer_ht_caps);
+       cmd->peer_max_mpdu      = __cpu_to_le32(arg->peer_max_mpdu);
+       cmd->peer_mpdu_density  = __cpu_to_le32(arg->peer_mpdu_density);
+       cmd->peer_rate_caps     = __cpu_to_le32(arg->peer_rate_caps);
+       cmd->peer_nss           = __cpu_to_le32(arg->peer_num_spatial_streams);
+       cmd->peer_vht_caps      = __cpu_to_le32(arg->peer_vht_caps);
+       cmd->peer_phymode       = __cpu_to_le32(arg->peer_phymode);
+
+       memcpy(cmd->peer_macaddr.addr, arg->addr, ETH_ALEN);
+
+       cmd->peer_legacy_rates.num_rates =
+               __cpu_to_le32(arg->peer_legacy_rates.num_rates);
+       memcpy(cmd->peer_legacy_rates.rates, arg->peer_legacy_rates.rates,
+              arg->peer_legacy_rates.num_rates);
+
+       cmd->peer_ht_rates.num_rates =
+               __cpu_to_le32(arg->peer_ht_rates.num_rates);
+       memcpy(cmd->peer_ht_rates.rates, arg->peer_ht_rates.rates,
+              arg->peer_ht_rates.num_rates);
+
+       cmd->peer_vht_rates.rx_max_rate =
+               __cpu_to_le32(arg->peer_vht_rates.rx_max_rate);
+       cmd->peer_vht_rates.rx_mcs_set =
+               __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set);
+       cmd->peer_vht_rates.tx_max_rate =
+               __cpu_to_le32(arg->peer_vht_rates.tx_max_rate);
+       cmd->peer_vht_rates.tx_mcs_set =
+               __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
+}
+
+int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg)
+{
+       struct wmi_bcn_tx_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_bcn_tx_cmd *)skb->data;
+       cmd->hdr.vdev_id  = __cpu_to_le32(arg->vdev_id);
+       cmd->hdr.tx_rate  = __cpu_to_le32(arg->tx_rate);
+       cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power);
+       cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
+       memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
+
+       return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID);
+}
+
+static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
+                                         const struct wmi_wmm_params_arg *arg)
+{
+       params->cwmin  = __cpu_to_le32(arg->cwmin);
+       params->cwmax  = __cpu_to_le32(arg->cwmax);
+       params->aifs   = __cpu_to_le32(arg->aifs);
+       params->txop   = __cpu_to_le32(arg->txop);
+       params->acm    = __cpu_to_le32(arg->acm);
+       params->no_ack = __cpu_to_le32(arg->no_ack);
+}
+
+int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+                       const struct wmi_pdev_set_wmm_params_arg *arg)
+{
+       struct wmi_pdev_set_wmm_params *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi);
+       ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
+       return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID);
+}
+
+int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
+{
+       struct wmi_request_stats_cmd *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_request_stats_cmd *)skb->data;
+       cmd->stats_id = __cpu_to_le32(stats_id);
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
+       return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
new file mode 100644 (file)
index 0000000..9555f5a
--- /dev/null
@@ -0,0 +1,3052 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-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 _WMI_H_
+#define _WMI_H_
+
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+/*
+ * This file specifies the WMI interface for the Unified Software
+ * Architecture.
+ *
+ * It includes definitions of all the commands and events. Commands are
+ * messages from the host to the target. Events and Replies are messages
+ * from the target to the host.
+ *
+ * Ownership of correctness in regards to WMI commands belongs to the host
+ * driver and the target is not required to validate parameters for value,
+ * proper range, or any other checking.
+ *
+ * Guidelines for extending this interface are below.
+ *
+ * 1. Add new WMI commands ONLY within the specified range - 0x9000 - 0x9fff
+ *
+ * 2. Use ONLY u32 type for defining member variables within WMI
+ *    command/event structures. Do not use u8, u16, bool or
+ *    enum types within these structures.
+ *
+ * 3. DO NOT define bit fields within structures. Implement bit fields
+ *    using masks if necessary. Do not use the programming language's bit
+ *    field definition.
+ *
+ * 4. Define macros for encode/decode of u8, u16 fields within
+ *    the u32 variables. Use these macros for set/get of these fields.
+ *    Try to use this to optimize the structure without bloating it with
+ *    u32 variables for every lower sized field.
+ *
+ * 5. Do not use PACK/UNPACK attributes for the structures as each member
+ *    variable is already 4-byte aligned by virtue of being a u32
+ *    type.
+ *
+ * 6. Comment each parameter part of the WMI command/event structure by
+ *    using the 2 stars at the begining of C comment instead of one star to
+ *    enable HTML document generation using Doxygen.
+ *
+ */
+
+/* Control Path */
+struct wmi_cmd_hdr {
+       __le32 cmd_id;
+} __packed;
+
+#define WMI_CMD_HDR_CMD_ID_MASK   0x00FFFFFF
+#define WMI_CMD_HDR_CMD_ID_LSB    0
+#define WMI_CMD_HDR_PLT_PRIV_MASK 0xFF000000
+#define WMI_CMD_HDR_PLT_PRIV_LSB  24
+
+#define HTC_PROTOCOL_VERSION    0x0002
+#define WMI_PROTOCOL_VERSION    0x0002
+
+enum wmi_service_id {
+       WMI_SERVICE_BEACON_OFFLOAD = 0,   /* beacon offload */
+       WMI_SERVICE_SCAN_OFFLOAD,         /* scan offload */
+       WMI_SERVICE_ROAM_OFFLOAD,         /* roam offload */
+       WMI_SERVICE_BCN_MISS_OFFLOAD,     /* beacon miss offload */
+       WMI_SERVICE_STA_PWRSAVE,          /* fake sleep + basic power save */
+       WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */
+       WMI_SERVICE_AP_UAPSD,             /* uapsd on AP */
+       WMI_SERVICE_AP_DFS,               /* DFS on AP */
+       WMI_SERVICE_11AC,                 /* supports 11ac */
+       WMI_SERVICE_BLOCKACK,   /* Supports triggering ADDBA/DELBA from host*/
+       WMI_SERVICE_PHYERR,               /* PHY error */
+       WMI_SERVICE_BCN_FILTER,           /* Beacon filter support */
+       WMI_SERVICE_RTT,                  /* RTT (round trip time) support */
+       WMI_SERVICE_RATECTRL,             /* Rate-control */
+       WMI_SERVICE_WOW,                  /* WOW Support */
+       WMI_SERVICE_RATECTRL_CACHE,       /* Rate-control caching */
+       WMI_SERVICE_IRAM_TIDS,            /* TIDs in IRAM */
+       WMI_SERVICE_ARPNS_OFFLOAD,        /* ARP NS Offload support */
+       WMI_SERVICE_NLO,                  /* Network list offload service */
+       WMI_SERVICE_GTK_OFFLOAD,          /* GTK offload */
+       WMI_SERVICE_SCAN_SCH,             /* Scan Scheduler Service */
+       WMI_SERVICE_CSA_OFFLOAD,          /* CSA offload service */
+       WMI_SERVICE_CHATTER,              /* Chatter service */
+       WMI_SERVICE_COEX_FREQAVOID,       /* FW report freq range to avoid */
+       WMI_SERVICE_PACKET_POWER_SAVE,    /* packet power save service */
+       WMI_SERVICE_FORCE_FW_HANG,        /* To test fw recovery mechanism */
+       WMI_SERVICE_GPIO,                 /* GPIO service */
+       WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */
+       WMI_STA_UAPSD_BASIC_AUTO_TRIG,    /* UAPSD AC Trigger Generation  */
+       WMI_STA_UAPSD_VAR_AUTO_TRIG,      /* -do- */
+       WMI_SERVICE_STA_KEEP_ALIVE,       /* STA keep alive mechanism support */
+       WMI_SERVICE_TX_ENCAP,             /* Packet type for TX encapsulation */
+
+       WMI_SERVICE_LAST,
+       WMI_MAX_SERVICE = 64              /* max service */
+};
+
+static inline char *wmi_service_name(int service_id)
+{
+       switch (service_id) {
+       case WMI_SERVICE_BEACON_OFFLOAD:
+               return "BEACON_OFFLOAD";
+       case WMI_SERVICE_SCAN_OFFLOAD:
+               return "SCAN_OFFLOAD";
+       case WMI_SERVICE_ROAM_OFFLOAD:
+               return "ROAM_OFFLOAD";
+       case WMI_SERVICE_BCN_MISS_OFFLOAD:
+               return "BCN_MISS_OFFLOAD";
+       case WMI_SERVICE_STA_PWRSAVE:
+               return "STA_PWRSAVE";
+       case WMI_SERVICE_STA_ADVANCED_PWRSAVE:
+               return "STA_ADVANCED_PWRSAVE";
+       case WMI_SERVICE_AP_UAPSD:
+               return "AP_UAPSD";
+       case WMI_SERVICE_AP_DFS:
+               return "AP_DFS";
+       case WMI_SERVICE_11AC:
+               return "11AC";
+       case WMI_SERVICE_BLOCKACK:
+               return "BLOCKACK";
+       case WMI_SERVICE_PHYERR:
+               return "PHYERR";
+       case WMI_SERVICE_BCN_FILTER:
+               return "BCN_FILTER";
+       case WMI_SERVICE_RTT:
+               return "RTT";
+       case WMI_SERVICE_RATECTRL:
+               return "RATECTRL";
+       case WMI_SERVICE_WOW:
+               return "WOW";
+       case WMI_SERVICE_RATECTRL_CACHE:
+               return "RATECTRL CACHE";
+       case WMI_SERVICE_IRAM_TIDS:
+               return "IRAM TIDS";
+       case WMI_SERVICE_ARPNS_OFFLOAD:
+               return "ARPNS_OFFLOAD";
+       case WMI_SERVICE_NLO:
+               return "NLO";
+       case WMI_SERVICE_GTK_OFFLOAD:
+               return "GTK_OFFLOAD";
+       case WMI_SERVICE_SCAN_SCH:
+               return "SCAN_SCH";
+       case WMI_SERVICE_CSA_OFFLOAD:
+               return "CSA_OFFLOAD";
+       case WMI_SERVICE_CHATTER:
+               return "CHATTER";
+       case WMI_SERVICE_COEX_FREQAVOID:
+               return "COEX_FREQAVOID";
+       case WMI_SERVICE_PACKET_POWER_SAVE:
+               return "PACKET_POWER_SAVE";
+       case WMI_SERVICE_FORCE_FW_HANG:
+               return "FORCE FW HANG";
+       case WMI_SERVICE_GPIO:
+               return "GPIO";
+       case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM:
+               return "MODULATED DTIM";
+       case WMI_STA_UAPSD_BASIC_AUTO_TRIG:
+               return "BASIC UAPSD";
+       case WMI_STA_UAPSD_VAR_AUTO_TRIG:
+               return "VAR UAPSD";
+       case WMI_SERVICE_STA_KEEP_ALIVE:
+               return "STA KEEP ALIVE";
+       case WMI_SERVICE_TX_ENCAP:
+               return "TX ENCAP";
+       default:
+               return "UNKNOWN SERVICE\n";
+       }
+}
+
+
+#define WMI_SERVICE_BM_SIZE \
+       ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32))
+
+/* 2 word representation of MAC addr */
+struct wmi_mac_addr {
+       union {
+               u8 addr[6];
+               struct {
+                       u32 word0;
+                       u32 word1;
+               } __packed;
+       } __packed;
+} __packed;
+
+/* macro to convert MAC address from WMI word format to char array */
+#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \
+       (c_macaddr)[0] =  ((pwmi_mac_addr)->word0) & 0xff; \
+       (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \
+       (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \
+       (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \
+       (c_macaddr)[4] =  ((pwmi_mac_addr)->word1) & 0xff; \
+       (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
+       } while (0)
+
+/*
+ * wmi command groups.
+ */
+enum wmi_cmd_group {
+       /* 0 to 2 are reserved */
+       WMI_GRP_START = 0x3,
+       WMI_GRP_SCAN = WMI_GRP_START,
+       WMI_GRP_PDEV,
+       WMI_GRP_VDEV,
+       WMI_GRP_PEER,
+       WMI_GRP_MGMT,
+       WMI_GRP_BA_NEG,
+       WMI_GRP_STA_PS,
+       WMI_GRP_DFS,
+       WMI_GRP_ROAM,
+       WMI_GRP_OFL_SCAN,
+       WMI_GRP_P2P,
+       WMI_GRP_AP_PS,
+       WMI_GRP_RATE_CTRL,
+       WMI_GRP_PROFILE,
+       WMI_GRP_SUSPEND,
+       WMI_GRP_BCN_FILTER,
+       WMI_GRP_WOW,
+       WMI_GRP_RTT,
+       WMI_GRP_SPECTRAL,
+       WMI_GRP_STATS,
+       WMI_GRP_ARP_NS_OFL,
+       WMI_GRP_NLO_OFL,
+       WMI_GRP_GTK_OFL,
+       WMI_GRP_CSA_OFL,
+       WMI_GRP_CHATTER,
+       WMI_GRP_TID_ADDBA,
+       WMI_GRP_MISC,
+       WMI_GRP_GPIO,
+};
+
+#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
+#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
+
+/* Command IDs and commande events. */
+enum wmi_cmd_id {
+       WMI_INIT_CMDID = 0x1,
+
+       /* Scan specific commands */
+       WMI_START_SCAN_CMDID = WMI_CMD_GRP(WMI_GRP_SCAN),
+       WMI_STOP_SCAN_CMDID,
+       WMI_SCAN_CHAN_LIST_CMDID,
+       WMI_SCAN_SCH_PRIO_TBL_CMDID,
+
+       /* PDEV (physical device) specific commands */
+       WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_CMD_GRP(WMI_GRP_PDEV),
+       WMI_PDEV_SET_CHANNEL_CMDID,
+       WMI_PDEV_SET_PARAM_CMDID,
+       WMI_PDEV_PKTLOG_ENABLE_CMDID,
+       WMI_PDEV_PKTLOG_DISABLE_CMDID,
+       WMI_PDEV_SET_WMM_PARAMS_CMDID,
+       WMI_PDEV_SET_HT_CAP_IE_CMDID,
+       WMI_PDEV_SET_VHT_CAP_IE_CMDID,
+       WMI_PDEV_SET_DSCP_TID_MAP_CMDID,
+       WMI_PDEV_SET_QUIET_MODE_CMDID,
+       WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
+       WMI_PDEV_GET_TPC_CONFIG_CMDID,
+       WMI_PDEV_SET_BASE_MACADDR_CMDID,
+
+       /* VDEV (virtual device) specific commands */
+       WMI_VDEV_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_VDEV),
+       WMI_VDEV_DELETE_CMDID,
+       WMI_VDEV_START_REQUEST_CMDID,
+       WMI_VDEV_RESTART_REQUEST_CMDID,
+       WMI_VDEV_UP_CMDID,
+       WMI_VDEV_STOP_CMDID,
+       WMI_VDEV_DOWN_CMDID,
+       WMI_VDEV_SET_PARAM_CMDID,
+       WMI_VDEV_INSTALL_KEY_CMDID,
+
+       /* peer specific commands */
+       WMI_PEER_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_PEER),
+       WMI_PEER_DELETE_CMDID,
+       WMI_PEER_FLUSH_TIDS_CMDID,
+       WMI_PEER_SET_PARAM_CMDID,
+       WMI_PEER_ASSOC_CMDID,
+       WMI_PEER_ADD_WDS_ENTRY_CMDID,
+       WMI_PEER_REMOVE_WDS_ENTRY_CMDID,
+       WMI_PEER_MCAST_GROUP_CMDID,
+
+       /* beacon/management specific commands */
+       WMI_BCN_TX_CMDID = WMI_CMD_GRP(WMI_GRP_MGMT),
+       WMI_PDEV_SEND_BCN_CMDID,
+       WMI_BCN_TMPL_CMDID,
+       WMI_BCN_FILTER_RX_CMDID,
+       WMI_PRB_REQ_FILTER_RX_CMDID,
+       WMI_MGMT_TX_CMDID,
+       WMI_PRB_TMPL_CMDID,
+
+       /* commands to directly control BA negotiation directly from host. */
+       WMI_ADDBA_CLEAR_RESP_CMDID = WMI_CMD_GRP(WMI_GRP_BA_NEG),
+       WMI_ADDBA_SEND_CMDID,
+       WMI_ADDBA_STATUS_CMDID,
+       WMI_DELBA_SEND_CMDID,
+       WMI_ADDBA_SET_RESP_CMDID,
+       WMI_SEND_SINGLEAMSDU_CMDID,
+
+       /* Station power save specific config */
+       WMI_STA_POWERSAVE_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_STA_PS),
+       WMI_STA_POWERSAVE_PARAM_CMDID,
+       WMI_STA_MIMO_PS_MODE_CMDID,
+
+       /** DFS-specific commands */
+       WMI_PDEV_DFS_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_DFS),
+       WMI_PDEV_DFS_DISABLE_CMDID,
+
+       /* Roaming specific  commands */
+       WMI_ROAM_SCAN_MODE = WMI_CMD_GRP(WMI_GRP_ROAM),
+       WMI_ROAM_SCAN_RSSI_THRESHOLD,
+       WMI_ROAM_SCAN_PERIOD,
+       WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
+       WMI_ROAM_AP_PROFILE,
+
+       /* offload scan specific commands */
+       WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_CMD_GRP(WMI_GRP_OFL_SCAN),
+       WMI_OFL_SCAN_REMOVE_AP_PROFILE,
+       WMI_OFL_SCAN_PERIOD,
+
+       /* P2P specific commands */
+       WMI_P2P_DEV_SET_DEVICE_INFO = WMI_CMD_GRP(WMI_GRP_P2P),
+       WMI_P2P_DEV_SET_DISCOVERABILITY,
+       WMI_P2P_GO_SET_BEACON_IE,
+       WMI_P2P_GO_SET_PROBE_RESP_IE,
+       WMI_P2P_SET_VENDOR_IE_DATA_CMDID,
+
+       /* AP power save specific config */
+       WMI_AP_PS_PEER_PARAM_CMDID = WMI_CMD_GRP(WMI_GRP_AP_PS),
+       WMI_AP_PS_PEER_UAPSD_COEX_CMDID,
+
+       /* Rate-control specific commands */
+       WMI_PEER_RATE_RETRY_SCHED_CMDID =
+       WMI_CMD_GRP(WMI_GRP_RATE_CTRL),
+
+       /* WLAN Profiling commands. */
+       WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_CMD_GRP(WMI_GRP_PROFILE),
+       WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
+       WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
+       WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
+       WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
+
+       /* Suspend resume command Ids */
+       WMI_PDEV_SUSPEND_CMDID = WMI_CMD_GRP(WMI_GRP_SUSPEND),
+       WMI_PDEV_RESUME_CMDID,
+
+       /* Beacon filter commands */
+       WMI_ADD_BCN_FILTER_CMDID = WMI_CMD_GRP(WMI_GRP_BCN_FILTER),
+       WMI_RMV_BCN_FILTER_CMDID,
+
+       /* WOW Specific WMI commands*/
+       WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_CMD_GRP(WMI_GRP_WOW),
+       WMI_WOW_DEL_WAKE_PATTERN_CMDID,
+       WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
+       WMI_WOW_ENABLE_CMDID,
+       WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
+
+       /* RTT measurement related cmd */
+       WMI_RTT_MEASREQ_CMDID = WMI_CMD_GRP(WMI_GRP_RTT),
+       WMI_RTT_TSF_CMDID,
+
+       /* spectral scan commands */
+       WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_CMD_GRP(WMI_GRP_SPECTRAL),
+       WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
+
+       /* F/W stats */
+       WMI_REQUEST_STATS_CMDID = WMI_CMD_GRP(WMI_GRP_STATS),
+
+       /* ARP OFFLOAD REQUEST*/
+       WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_ARP_NS_OFL),
+
+       /* NS offload confid*/
+       WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_NLO_OFL),
+
+       /* GTK offload Specific WMI commands*/
+       WMI_GTK_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_GTK_OFL),
+
+       /* CSA offload Specific WMI commands*/
+       WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_CSA_OFL),
+       WMI_CSA_OFFLOAD_CHANSWITCH_CMDID,
+
+       /* Chatter commands*/
+       WMI_CHATTER_SET_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_CHATTER),
+
+       /* addba specific commands */
+       WMI_PEER_TID_ADDBA_CMDID = WMI_CMD_GRP(WMI_GRP_TID_ADDBA),
+       WMI_PEER_TID_DELBA_CMDID,
+
+       /* set station mimo powersave method */
+       WMI_STA_DTIM_PS_METHOD_CMDID,
+       /* Configure the Station UAPSD AC Auto Trigger Parameters */
+       WMI_STA_UAPSD_AUTO_TRIG_CMDID,
+
+       /* STA Keep alive parameter configuration,
+          Requires WMI_SERVICE_STA_KEEP_ALIVE */
+       WMI_STA_KEEPALIVE_CMD,
+
+       /* misc command group */
+       WMI_ECHO_CMDID = WMI_CMD_GRP(WMI_GRP_MISC),
+       WMI_PDEV_UTF_CMDID,
+       WMI_DBGLOG_CFG_CMDID,
+       WMI_PDEV_QVIT_CMDID,
+       WMI_PDEV_FTM_INTG_CMDID,
+       WMI_VDEV_SET_KEEPALIVE_CMDID,
+       WMI_VDEV_GET_KEEPALIVE_CMDID,
+
+       /* GPIO Configuration */
+       WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
+       WMI_GPIO_OUTPUT_CMDID,
+};
+
+enum wmi_event_id {
+       WMI_SERVICE_READY_EVENTID = 0x1,
+       WMI_READY_EVENTID,
+
+       /* Scan specific events */
+       WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN),
+
+       /* PDEV specific events */
+       WMI_PDEV_TPC_CONFIG_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PDEV),
+       WMI_CHAN_INFO_EVENTID,
+       WMI_PHYERR_EVENTID,
+
+       /* VDEV specific events */
+       WMI_VDEV_START_RESP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_VDEV),
+       WMI_VDEV_STOPPED_EVENTID,
+       WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
+
+       /* peer specific events */
+       WMI_PEER_STA_KICKOUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PEER),
+
+       /* beacon/mgmt specific events */
+       WMI_MGMT_RX_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MGMT),
+       WMI_HOST_SWBA_EVENTID,
+       WMI_TBTTOFFSET_UPDATE_EVENTID,
+
+       /* ADDBA Related WMI Events*/
+       WMI_TX_DELBA_COMPLETE_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_BA_NEG),
+       WMI_TX_ADDBA_COMPLETE_EVENTID,
+
+       /* Roam event to trigger roaming on host */
+       WMI_ROAM_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_ROAM),
+       WMI_PROFILE_MATCH,
+
+       /* WoW */
+       WMI_WOW_WAKEUP_HOST_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_WOW),
+
+       /* RTT */
+       WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_RTT),
+       WMI_TSF_MEASUREMENT_REPORT_EVENTID,
+       WMI_RTT_ERROR_REPORT_EVENTID,
+
+       /* GTK offload */
+       WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GTK_OFL),
+       WMI_GTK_REKEY_FAIL_EVENTID,
+
+       /* CSA IE received event */
+       WMI_CSA_HANDLING_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_CSA_OFL),
+
+       /* Misc events */
+       WMI_ECHO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MISC),
+       WMI_PDEV_UTF_EVENTID,
+       WMI_DEBUG_MESG_EVENTID,
+       WMI_UPDATE_STATS_EVENTID,
+       WMI_DEBUG_PRINT_EVENTID,
+       WMI_DCS_INTERFERENCE_EVENTID,
+       WMI_PDEV_QVIT_EVENTID,
+       WMI_WLAN_PROFILE_DATA_EVENTID,
+       WMI_PDEV_FTM_INTG_EVENTID,
+       WMI_WLAN_FREQ_AVOID_EVENTID,
+       WMI_VDEV_GET_KEEPALIVE_EVENTID,
+
+       /* GPIO Event */
+       WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
+};
+
+enum wmi_phy_mode {
+       MODE_11A        = 0,   /* 11a Mode */
+       MODE_11G        = 1,   /* 11b/g Mode */
+       MODE_11B        = 2,   /* 11b Mode */
+       MODE_11GONLY    = 3,   /* 11g only Mode */
+       MODE_11NA_HT20   = 4,  /* 11a HT20 mode */
+       MODE_11NG_HT20   = 5,  /* 11g HT20 mode */
+       MODE_11NA_HT40   = 6,  /* 11a HT40 mode */
+       MODE_11NG_HT40   = 7,  /* 11g HT40 mode */
+       MODE_11AC_VHT20 = 8,
+       MODE_11AC_VHT40 = 9,
+       MODE_11AC_VHT80 = 10,
+       /*    MODE_11AC_VHT160 = 11, */
+       MODE_11AC_VHT20_2G = 11,
+       MODE_11AC_VHT40_2G = 12,
+       MODE_11AC_VHT80_2G = 13,
+       MODE_UNKNOWN    = 14,
+       MODE_MAX        = 14
+};
+
+#define WMI_CHAN_LIST_TAG      0x1
+#define WMI_SSID_LIST_TAG      0x2
+#define WMI_BSSID_LIST_TAG     0x3
+#define WMI_IE_TAG             0x4
+
+struct wmi_channel {
+       __le32 mhz;
+       __le32 band_center_freq1;
+       __le32 band_center_freq2; /* valid for 11ac, 80plus80 */
+       union {
+               __le32 flags; /* WMI_CHAN_FLAG_ */
+               struct {
+                       u8 mode; /* only 6 LSBs */
+               } __packed;
+       } __packed;
+       union {
+               __le32 reginfo0;
+               struct {
+                       u8 min_power;
+                       u8 max_power;
+                       u8 reg_power;
+                       u8 reg_classid;
+               } __packed;
+       } __packed;
+       union {
+               __le32 reginfo1;
+               struct {
+                       u8 antenna_max;
+               } __packed;
+       } __packed;
+} __packed;
+
+struct wmi_channel_arg {
+       u32 freq;
+       u32 band_center_freq1;
+       bool passive;
+       bool allow_ibss;
+       bool allow_ht;
+       bool allow_vht;
+       bool ht40plus;
+       /* note: power unit is 1/4th of dBm */
+       u32 min_power;
+       u32 max_power;
+       u32 max_reg_power;
+       u32 max_antenna_gain;
+       u32 reg_class_id;
+       enum wmi_phy_mode mode;
+};
+
+enum wmi_channel_change_cause {
+       WMI_CHANNEL_CHANGE_CAUSE_NONE = 0,
+       WMI_CHANNEL_CHANGE_CAUSE_CSA,
+};
+
+#define WMI_CHAN_FLAG_HT40_PLUS      (1 << 6)
+#define WMI_CHAN_FLAG_PASSIVE        (1 << 7)
+#define WMI_CHAN_FLAG_ADHOC_ALLOWED  (1 << 8)
+#define WMI_CHAN_FLAG_AP_DISABLED    (1 << 9)
+#define WMI_CHAN_FLAG_DFS            (1 << 10)
+#define WMI_CHAN_FLAG_ALLOW_HT       (1 << 11)
+#define WMI_CHAN_FLAG_ALLOW_VHT      (1 << 12)
+
+/* Indicate reason for channel switch */
+#define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13)
+
+#define WMI_MAX_SPATIAL_STREAM   3
+
+/* HT Capabilities*/
+#define WMI_HT_CAP_ENABLED                0x0001   /* HT Enabled/ disabled */
+#define WMI_HT_CAP_HT20_SGI       0x0002   /* Short Guard Interval with HT20 */
+#define WMI_HT_CAP_DYNAMIC_SMPS           0x0004   /* Dynamic MIMO powersave */
+#define WMI_HT_CAP_TX_STBC                0x0008   /* B3 TX STBC */
+#define WMI_HT_CAP_TX_STBC_MASK_SHIFT     3
+#define WMI_HT_CAP_RX_STBC                0x0030   /* B4-B5 RX STBC */
+#define WMI_HT_CAP_RX_STBC_MASK_SHIFT     4
+#define WMI_HT_CAP_LDPC                   0x0040   /* LDPC supported */
+#define WMI_HT_CAP_L_SIG_TXOP_PROT        0x0080   /* L-SIG TXOP Protection */
+#define WMI_HT_CAP_MPDU_DENSITY           0x0700   /* MPDU Density */
+#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8
+#define WMI_HT_CAP_HT40_SGI               0x0800
+
+#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED       | \
+                               WMI_HT_CAP_HT20_SGI      | \
+                               WMI_HT_CAP_HT40_SGI      | \
+                               WMI_HT_CAP_TX_STBC       | \
+                               WMI_HT_CAP_RX_STBC       | \
+                               WMI_HT_CAP_LDPC)
+
+
+/*
+ * WMI_VHT_CAP_* these maps to ieee 802.11ac vht capability information
+ * field. The fields not defined here are not supported, or reserved.
+ * Do not change these masks and if you have to add new one follow the
+ * bitmask as specified by 802.11ac draft.
+ */
+
+#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK            0x00000003
+#define WMI_VHT_CAP_RX_LDPC                      0x00000010
+#define WMI_VHT_CAP_SGI_80MHZ                    0x00000020
+#define WMI_VHT_CAP_TX_STBC                      0x00000080
+#define WMI_VHT_CAP_RX_STBC_MASK                 0x00000300
+#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT           8
+#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP            0x03800000
+#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT      23
+#define WMI_VHT_CAP_RX_FIXED_ANT                 0x10000000
+#define WMI_VHT_CAP_TX_FIXED_ANT                 0x20000000
+
+/* The following also refer for max HT AMSDU */
+#define WMI_VHT_CAP_MAX_MPDU_LEN_3839            0x00000000
+#define WMI_VHT_CAP_MAX_MPDU_LEN_7935            0x00000001
+#define WMI_VHT_CAP_MAX_MPDU_LEN_11454           0x00000002
+
+#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454  | \
+                                WMI_VHT_CAP_RX_LDPC             | \
+                                WMI_VHT_CAP_SGI_80MHZ           | \
+                                WMI_VHT_CAP_TX_STBC             | \
+                                WMI_VHT_CAP_RX_STBC_MASK        | \
+                                WMI_VHT_CAP_MAX_AMPDU_LEN_EXP   | \
+                                WMI_VHT_CAP_RX_FIXED_ANT        | \
+                                WMI_VHT_CAP_TX_FIXED_ANT)
+
+/*
+ * Interested readers refer to Rx/Tx MCS Map definition as defined in
+ * 802.11ac
+ */
+#define WMI_VHT_MAX_MCS_4_SS_MASK(r, ss)      ((3 & (r)) << (((ss) - 1) << 1))
+#define WMI_VHT_MAX_SUPP_RATE_MASK           0x1fff0000
+#define WMI_VHT_MAX_SUPP_RATE_MASK_SHIFT     16
+
+enum {
+       REGDMN_MODE_11A              = 0x00001, /* 11a channels */
+       REGDMN_MODE_TURBO            = 0x00002, /* 11a turbo-only channels */
+       REGDMN_MODE_11B              = 0x00004, /* 11b channels */
+       REGDMN_MODE_PUREG            = 0x00008, /* 11g channels (OFDM only) */
+       REGDMN_MODE_11G              = 0x00008, /* XXX historical */
+       REGDMN_MODE_108G             = 0x00020, /* 11a+Turbo channels */
+       REGDMN_MODE_108A             = 0x00040, /* 11g+Turbo channels */
+       REGDMN_MODE_XR               = 0x00100, /* XR channels */
+       REGDMN_MODE_11A_HALF_RATE    = 0x00200, /* 11A half rate channels */
+       REGDMN_MODE_11A_QUARTER_RATE = 0x00400, /* 11A quarter rate channels */
+       REGDMN_MODE_11NG_HT20        = 0x00800, /* 11N-G HT20 channels */
+       REGDMN_MODE_11NA_HT20        = 0x01000, /* 11N-A HT20 channels */
+       REGDMN_MODE_11NG_HT40PLUS    = 0x02000, /* 11N-G HT40 + channels */
+       REGDMN_MODE_11NG_HT40MINUS   = 0x04000, /* 11N-G HT40 - channels */
+       REGDMN_MODE_11NA_HT40PLUS    = 0x08000, /* 11N-A HT40 + channels */
+       REGDMN_MODE_11NA_HT40MINUS   = 0x10000, /* 11N-A HT40 - channels */
+       REGDMN_MODE_11AC_VHT20       = 0x20000, /* 5Ghz, VHT20 */
+       REGDMN_MODE_11AC_VHT40PLUS   = 0x40000, /* 5Ghz, VHT40 + channels */
+       REGDMN_MODE_11AC_VHT40MINUS  = 0x80000, /* 5Ghz  VHT40 - channels */
+       REGDMN_MODE_11AC_VHT80       = 0x100000, /* 5Ghz, VHT80 channels */
+       REGDMN_MODE_ALL              = 0xffffffff
+};
+
+#define REGDMN_CAP1_CHAN_HALF_RATE        0x00000001
+#define REGDMN_CAP1_CHAN_QUARTER_RATE     0x00000002
+#define REGDMN_CAP1_CHAN_HAL49GHZ         0x00000004
+
+/* regulatory capabilities */
+#define REGDMN_EEPROM_EEREGCAP_EN_FCC_MIDBAND   0x0040
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_EVEN    0x0080
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U2         0x0100
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_MIDBAND    0x0200
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_ODD     0x0400
+#define REGDMN_EEPROM_EEREGCAP_EN_KK_NEW_11A    0x0800
+
+struct hal_reg_capabilities {
+       /* regdomain value specified in EEPROM */
+       __le32 eeprom_rd;
+       /*regdomain */
+       __le32 eeprom_rd_ext;
+       /* CAP1 capabilities bit map. */
+       __le32 regcap1;
+       /* REGDMN EEPROM CAP. */
+       __le32 regcap2;
+       /* REGDMN MODE */
+       __le32 wireless_modes;
+       __le32 low_2ghz_chan;
+       __le32 high_2ghz_chan;
+       __le32 low_5ghz_chan;
+       __le32 high_5ghz_chan;
+} __packed;
+
+enum wlan_mode_capability {
+       WHAL_WLAN_11A_CAPABILITY   = 0x1,
+       WHAL_WLAN_11G_CAPABILITY   = 0x2,
+       WHAL_WLAN_11AG_CAPABILITY  = 0x3,
+};
+
+/* structure used by FW for requesting host memory */
+struct wlan_host_mem_req {
+       /* ID of the request */
+       __le32 req_id;
+       /* size of the  of each unit */
+       __le32 unit_size;
+       /* flags to  indicate that
+        * the number units is dependent
+        * on number of resources(num vdevs num peers .. etc)
+        */
+       __le32 num_unit_info;
+       /*
+        * actual number of units to allocate . if flags in the num_unit_info
+        * indicate that number of units is tied to number of a particular
+        * resource to allocate then  num_units filed is set to 0 and host
+        * will derive the number units from number of the resources it is
+        * requesting.
+        */
+       __le32 num_units;
+} __packed;
+
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
+       ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+       (1 << ((svc_id)%(sizeof(u32))))) != 0)
+
+/*
+ * The following struct holds optional payload for
+ * wmi_service_ready_event,e.g., 11ac pass some of the
+ * device capability to the host.
+ */
+struct wmi_service_ready_event {
+       __le32 sw_version;
+       __le32 sw_version_1;
+       __le32 abi_version;
+       /* WMI_PHY_CAPABILITY */
+       __le32 phy_capability;
+       /* Maximum number of frag table entries that SW will populate less 1 */
+       __le32 max_frag_entry;
+       __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+       __le32 num_rf_chains;
+       /*
+        * The following field is only valid for service type
+        * WMI_SERVICE_11AC
+        */
+       __le32 ht_cap_info; /* WMI HT Capability */
+       __le32 vht_cap_info; /* VHT capability info field of 802.11ac */
+       __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */
+       __le32 hw_min_tx_power;
+       __le32 hw_max_tx_power;
+       struct hal_reg_capabilities hal_reg_capabilities;
+       __le32 sys_cap_info;
+       __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */
+       /*
+        * Max beacon and Probe Response IE offload size
+        * (includes optional P2P IEs)
+        */
+       __le32 max_bcn_ie_size;
+       /*
+        * request to host to allocate a chuck of memory and pss it down to FW
+        * via WM_INIT. FW uses this as FW extesnsion memory for saving its
+        * data structures. Only valid for low latency interfaces like PCIE
+        * where FW can access this memory directly (or) by DMA.
+        */
+       __le32 num_mem_reqs;
+       struct wlan_host_mem_req mem_reqs[1];
+} __packed;
+
+/*
+ * status consists of  upper 16 bits fo int status and lower 16 bits of
+ * module ID that retuned status
+ */
+#define WLAN_INIT_STATUS_SUCCESS   0x0
+#define WLAN_GET_INIT_STATUS_REASON(status)    ((status) & 0xffff)
+#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff)
+
+#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
+#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ)
+
+struct wmi_ready_event {
+       __le32 sw_version;
+       __le32 abi_version;
+       struct wmi_mac_addr mac_addr;
+       __le32 status;
+} __packed;
+
+struct wmi_resource_config {
+       /* number of virtual devices (VAPs) to support */
+       __le32 num_vdevs;
+
+       /* number of peer nodes to support */
+       __le32 num_peers;
+
+       /*
+        * In offload mode target supports features like WOW, chatter and
+        * other protocol offloads. In order to support them some
+        * functionalities like reorder buffering, PN checking need to be
+        * done in target. This determines maximum number of peers suported
+        * by target in offload mode
+        */
+       __le32 num_offload_peers;
+
+       /* For target-based RX reordering */
+       __le32 num_offload_reorder_bufs;
+
+       /* number of keys per peer */
+       __le32 num_peer_keys;
+
+       /* total number of TX/RX data TIDs */
+       __le32 num_tids;
+
+       /*
+        * max skid for resolving hash collisions
+        *
+        *   The address search table is sparse, so that if two MAC addresses
+        *   result in the same hash value, the second of these conflicting
+        *   entries can slide to the next index in the address search table,
+        *   and use it, if it is unoccupied.  This ast_skid_limit parameter
+        *   specifies the upper bound on how many subsequent indices to search
+        *   over to find an unoccupied space.
+        */
+       __le32 ast_skid_limit;
+
+       /*
+        * the nominal chain mask for transmit
+        *
+        *   The chain mask may be modified dynamically, e.g. to operate AP
+        *   tx with a reduced number of chains if no clients are associated.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of tx chains.
+        */
+       __le32 tx_chain_mask;
+
+       /*
+        * the nominal chain mask for receive
+        *
+        *   The chain mask may be modified dynamically, e.g. for a client
+        *   to use a reduced number of chains for receive if the traffic to
+        *   the client is low enough that it doesn't require downlink MIMO
+        *   or antenna diversity.
+        *   This configuration parameter specifies the nominal chain-mask that
+        *   should be used when not operating with a reduced set of rx chains.
+        */
+       __le32 rx_chain_mask;
+
+       /*
+        * what rx reorder timeout (ms) to use for the AC
+        *
+        *   Each WMM access class (voice, video, best-effort, background) will
+        *   have its own timeout value to dictate how long to wait for missing
+        *   rx MPDUs to arrive before flushing subsequent MPDUs that have
+        *   already been received.
+        *   This parameter specifies the timeout in milliseconds for each
+        *   class.
+        */
+       __le32 rx_timeout_pri_vi;
+       __le32 rx_timeout_pri_vo;
+       __le32 rx_timeout_pri_be;
+       __le32 rx_timeout_pri_bk;
+
+       /*
+        * what mode the rx should decap packets to
+        *
+        *   MAC can decap to RAW (no decap), native wifi or Ethernet types
+        *   THis setting also determines the default TX behavior, however TX
+        *   behavior can be modified on a per VAP basis during VAP init
+        */
+       __le32 rx_decap_mode;
+
+       /* what is the maximum scan requests than can be queued */
+       __le32 scan_max_pending_reqs;
+
+       /* maximum VDEV that could use BMISS offload */
+       __le32 bmiss_offload_max_vdev;
+
+       /* maximum VDEV that could use offload roaming */
+       __le32 roam_offload_max_vdev;
+
+       /* maximum AP profiles that would push to offload roaming */
+       __le32 roam_offload_max_ap_profiles;
+
+       /*
+        * how many groups to use for mcast->ucast conversion
+        *
+        *   The target's WAL maintains a table to hold information regarding
+        *   which peers belong to a given multicast group, so that if
+        *   multicast->unicast conversion is enabled, the target can convert
+        *   multicast tx frames to a series of unicast tx frames, to each
+        *   peer within the multicast group.
+            This num_mcast_groups configuration parameter tells the target how
+        *   many multicast groups to provide storage for within its multicast
+        *   group membership table.
+        */
+       __le32 num_mcast_groups;
+
+       /*
+        * size to alloc for the mcast membership table
+        *
+        *   This num_mcast_table_elems configuration parameter tells the
+        *   target how many peer elements it needs to provide storage for in
+        *   its multicast group membership table.
+        *   These multicast group membership table elements are shared by the
+        *   multicast groups stored within the table.
+        */
+       __le32 num_mcast_table_elems;
+
+       /*
+        * whether/how to do multicast->unicast conversion
+        *
+        *   This configuration parameter specifies whether the target should
+        *   perform multicast --> unicast conversion on transmit, and if so,
+        *   what to do if it finds no entries in its multicast group
+        *   membership table for the multicast IP address in the tx frame.
+        *   Configuration value:
+        *   0 -> Do not perform multicast to unicast conversion.
+        *   1 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        drop the frame.
+        *   2 -> Convert multicast frames to unicast, if the IP multicast
+        *        address from the tx frame is found in the multicast group
+        *        membership table.  If the IP multicast address is not found,
+        *        transmit the frame as multicast.
+        */
+       __le32 mcast2ucast_mode;
+
+       /*
+        * how much memory to allocate for a tx PPDU dbg log
+        *
+        *   This parameter controls how much memory the target will allocate
+        *   to store a log of tx PPDU meta-information (how large the PPDU
+        *   was, when it was sent, whether it was successful, etc.)
+        */
+       __le32 tx_dbg_log_size;
+
+       /* how many AST entries to be allocated for WDS */
+       __le32 num_wds_entries;
+
+       /*
+        * MAC DMA burst size, e.g., For target PCI limit can be
+        * 0 -default, 1 256B
+        */
+       __le32 dma_burst_size;
+
+       /*
+        * Fixed delimiters to be inserted after every MPDU to
+        * account for interface latency to avoid underrun.
+        */
+       __le32 mac_aggr_delim;
+
+       /*
+        *   determine whether target is responsible for detecting duplicate
+        *   non-aggregate MPDU and timing out stale fragments.
+        *
+        *   A-MPDU reordering is always performed on the target.
+        *
+        *   0: target responsible for frag timeout and dup checking
+        *   1: host responsible for frag timeout and dup checking
+        */
+       __le32 rx_skip_defrag_timeout_dup_detection_check;
+
+       /*
+        * Configuration for VoW :
+        * No of Video Nodes to be supported
+        * and Max no of descriptors for each Video link (node).
+        */
+       __le32 vow_config;
+
+       /* maximum VDEV that could use GTK offload */
+       __le32 gtk_offload_max_vdev;
+
+       /* Number of msdu descriptors target should use */
+       __le32 num_msdu_desc;
+
+       /*
+        * Max. number of Tx fragments per MSDU
+        *  This parameter controls the max number of Tx fragments per MSDU.
+        *  This is sent by the target as part of the WMI_SERVICE_READY event
+        *  and is overriden by the OS shim as required.
+        */
+       __le32 max_frag_entries;
+} __packed;
+
+/* strucutre describing host memory chunk. */
+struct host_memory_chunk {
+       /* id of the request that is passed up in service ready */
+       __le32 req_id;
+       /* the physical address the memory chunk */
+       __le32 ptr;
+       /* size of the chunk */
+       __le32 size;
+} __packed;
+
+struct wmi_init_cmd {
+       struct wmi_resource_config resource_config;
+       __le32 num_host_mem_chunks;
+
+       /*
+        * variable number of host memory chunks.
+        * This should be the last element in the structure
+        */
+       struct host_memory_chunk host_mem_chunks[1];
+} __packed;
+
+/* TLV for channel list */
+struct wmi_chan_list {
+       __le32 tag; /* WMI_CHAN_LIST_TAG */
+       __le32 num_chan;
+       __le32 channel_list[0];
+} __packed;
+
+struct wmi_bssid_list {
+       __le32 tag; /* WMI_BSSID_LIST_TAG */
+       __le32 num_bssid;
+       struct wmi_mac_addr bssid_list[0];
+} __packed;
+
+struct wmi_ie_data {
+       __le32 tag; /* WMI_IE_TAG */
+       __le32 ie_len;
+       u8 ie_data[0];
+} __packed;
+
+struct wmi_ssid {
+       __le32 ssid_len;
+       u8 ssid[32];
+} __packed;
+
+struct wmi_ssid_list {
+       __le32 tag; /* WMI_SSID_LIST_TAG */
+       __le32 num_ssids;
+       struct wmi_ssid ssids[0];
+} __packed;
+
+/* prefix used by scan requestor ids on the host */
+#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000
+
+/* prefix used by scan request ids generated on the host */
+/* host cycles through the lower 12 bits to generate ids */
+#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000
+
+#define WLAN_SCAN_PARAMS_MAX_SSID    16
+#define WLAN_SCAN_PARAMS_MAX_BSSID   4
+#define WLAN_SCAN_PARAMS_MAX_IE_LEN  256
+
+/* Scan priority numbers must be sequential, starting with 0 */
+enum wmi_scan_priority {
+       WMI_SCAN_PRIORITY_VERY_LOW = 0,
+       WMI_SCAN_PRIORITY_LOW,
+       WMI_SCAN_PRIORITY_MEDIUM,
+       WMI_SCAN_PRIORITY_HIGH,
+       WMI_SCAN_PRIORITY_VERY_HIGH,
+       WMI_SCAN_PRIORITY_COUNT   /* number of priorities supported */
+};
+
+struct wmi_start_scan_cmd {
+       /* Scan ID */
+       __le32 scan_id;
+       /* Scan requestor ID */
+       __le32 scan_req_id;
+       /* VDEV id(interface) that is requesting scan */
+       __le32 vdev_id;
+       /* Scan Priority, input to scan scheduler */
+       __le32 scan_priority;
+       /* Scan events subscription */
+       __le32 notify_scan_events;
+       /* dwell time in msec on active channels */
+       __le32 dwell_time_active;
+       /* dwell time in msec on passive channels */
+       __le32 dwell_time_passive;
+       /*
+        * min time in msec on the BSS channel,only valid if atleast one
+        * VDEV is active
+        */
+       __le32 min_rest_time;
+       /*
+        * max rest time in msec on the BSS channel,only valid if at least
+        * one VDEV is active
+        */
+       /*
+        * the scanner will rest on the bss channel at least min_rest_time
+        * after min_rest_time the scanner will start checking for tx/rx
+        * activity on all VDEVs. if there is no activity the scanner will
+        * switch to off channel. if there is activity the scanner will let
+        * the radio on the bss channel until max_rest_time expires.at
+        * max_rest_time scanner will switch to off channel irrespective of
+        * activity. activity is determined by the idle_time parameter.
+        */
+       __le32 max_rest_time;
+       /*
+        * time before sending next set of probe requests.
+        * The scanner keeps repeating probe requests transmission with
+        * period specified by repeat_probe_time.
+        * The number of probe requests specified depends on the ssid_list
+        * and bssid_list
+        */
+       __le32 repeat_probe_time;
+       /* time in msec between 2 consequetive probe requests with in a set. */
+       __le32 probe_spacing_time;
+       /*
+        * data inactivity time in msec on bss channel that will be used by
+        * scanner for measuring the inactivity.
+        */
+       __le32 idle_time;
+       /* maximum time in msec allowed for scan  */
+       __le32 max_scan_time;
+       /*
+        * delay in msec before sending first probe request after switching
+        * to a channel
+        */
+       __le32 probe_delay;
+       /* Scan control flags */
+       __le32 scan_ctrl_flags;
+
+       /* Burst duration time in msecs */
+       __le32 burst_duration;
+       /*
+        * TLV (tag length value )  paramerters follow the scan_cmd structure.
+        * TLV can contain channel list, bssid list, ssid list and
+        * ie. the TLV tags are defined above;
+        */
+} __packed;
+
+struct wmi_ssid_arg {
+       int len;
+       const u8 *ssid;
+};
+
+struct wmi_bssid_arg {
+       const u8 *bssid;
+};
+
+struct wmi_start_scan_arg {
+       u32 scan_id;
+       u32 scan_req_id;
+       u32 vdev_id;
+       u32 scan_priority;
+       u32 notify_scan_events;
+       u32 dwell_time_active;
+       u32 dwell_time_passive;
+       u32 min_rest_time;
+       u32 max_rest_time;
+       u32 repeat_probe_time;
+       u32 probe_spacing_time;
+       u32 idle_time;
+       u32 max_scan_time;
+       u32 probe_delay;
+       u32 scan_ctrl_flags;
+
+       u32 ie_len;
+       u32 n_channels;
+       u32 n_ssids;
+       u32 n_bssids;
+
+       u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN];
+       u32 channels[64];
+       struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID];
+       struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID];
+};
+
+/* scan control flags */
+
+/* passively scan all channels including active channels */
+#define WMI_SCAN_FLAG_PASSIVE        0x1
+/* add wild card ssid probe request even though ssid_list is specified. */
+#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2
+/* add cck rates to rates/xrate ie for the generated probe request */
+#define WMI_SCAN_ADD_CCK_RATES 0x4
+/* add ofdm rates to rates/xrate ie for the generated probe request */
+#define WMI_SCAN_ADD_OFDM_RATES 0x8
+/* To enable indication of Chan load and Noise floor to host */
+#define WMI_SCAN_CHAN_STAT_EVENT 0x10
+/* Filter Probe request frames  */
+#define WMI_SCAN_FILTER_PROBE_REQ 0x20
+/* When set, DFS channels will not be scanned */
+#define WMI_SCAN_BYPASS_DFS_CHN 0x40
+/* Different FW scan engine may choose to bail out on errors.
+ * Allow the driver to have influence over that. */
+#define WMI_SCAN_CONTINUE_ON_ERROR 0x80
+
+/* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */
+#define WMI_SCAN_CLASS_MASK 0xFF000000
+
+
+enum wmi_stop_scan_type {
+       WMI_SCAN_STOP_ONE       = 0x00000000, /* stop by scan_id */
+       WMI_SCAN_STOP_VDEV_ALL  = 0x01000000, /* stop by vdev_id */
+       WMI_SCAN_STOP_ALL       = 0x04000000, /* stop all scans */
+};
+
+struct wmi_stop_scan_cmd {
+       __le32 scan_req_id;
+       __le32 scan_id;
+       __le32 req_type;
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_stop_scan_arg {
+       u32 req_id;
+       enum wmi_stop_scan_type req_type;
+       union {
+               u32 scan_id;
+               u32 vdev_id;
+       } u;
+};
+
+struct wmi_scan_chan_list_cmd {
+       __le32 num_scan_chans;
+       struct wmi_channel chan_info[0];
+} __packed;
+
+struct wmi_scan_chan_list_arg {
+       u32 n_channels;
+       struct wmi_channel_arg *channels;
+};
+
+enum wmi_bss_filter {
+       WMI_BSS_FILTER_NONE = 0,        /* no beacons forwarded */
+       WMI_BSS_FILTER_ALL,             /* all beacons forwarded */
+       WMI_BSS_FILTER_PROFILE,         /* only beacons matching profile */
+       WMI_BSS_FILTER_ALL_BUT_PROFILE, /* all but beacons matching profile */
+       WMI_BSS_FILTER_CURRENT_BSS,     /* only beacons matching current BSS */
+       WMI_BSS_FILTER_ALL_BUT_BSS,     /* all but beacons matching BSS */
+       WMI_BSS_FILTER_PROBED_SSID,     /* beacons matching probed ssid */
+       WMI_BSS_FILTER_LAST_BSS,        /* marker only */
+};
+
+enum wmi_scan_event_type {
+       WMI_SCAN_EVENT_STARTED         = 0x1,
+       WMI_SCAN_EVENT_COMPLETED       = 0x2,
+       WMI_SCAN_EVENT_BSS_CHANNEL     = 0x4,
+       WMI_SCAN_EVENT_FOREIGN_CHANNEL = 0x8,
+       WMI_SCAN_EVENT_DEQUEUED        = 0x10,
+       WMI_SCAN_EVENT_PREEMPTED       = 0x20, /* possibly by high-prio scan */
+       WMI_SCAN_EVENT_START_FAILED    = 0x40,
+       WMI_SCAN_EVENT_RESTARTED       = 0x80,
+       WMI_SCAN_EVENT_MAX             = 0x8000
+};
+
+enum wmi_scan_completion_reason {
+       WMI_SCAN_REASON_COMPLETED,
+       WMI_SCAN_REASON_CANCELLED,
+       WMI_SCAN_REASON_PREEMPTED,
+       WMI_SCAN_REASON_TIMEDOUT,
+       WMI_SCAN_REASON_MAX,
+};
+
+struct wmi_scan_event {
+       __le32 event_type; /* %WMI_SCAN_EVENT_ */
+       __le32 reason; /* %WMI_SCAN_REASON_ */
+       __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */
+       __le32 scan_req_id;
+       __le32 scan_id;
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * This defines how much headroom is kept in the
+ * receive frame between the descriptor and the
+ * payload, in order for the WMI PHY error and
+ * management handler to insert header contents.
+ *
+ * This is in bytes.
+ */
+#define WMI_MGMT_RX_HDR_HEADROOM    52
+
+/*
+ * This event will be used for sending scan results
+ * as well as rx mgmt frames to the host. The rx buffer
+ * will be sent as part of this WMI event. It would be a
+ * good idea to pass all the fields in the RX status
+ * descriptor up to the host.
+ */
+struct wmi_mgmt_rx_hdr {
+       __le32 channel;
+       __le32 snr;
+       __le32 rate;
+       __le32 phy_mode;
+       __le32 buf_len;
+       __le32 status; /* %WMI_RX_STATUS_ */
+} __packed;
+
+struct wmi_mgmt_rx_event {
+       struct wmi_mgmt_rx_hdr hdr;
+       u8 buf[0];
+} __packed;
+
+#define WMI_RX_STATUS_OK                       0x00
+#define WMI_RX_STATUS_ERR_CRC                  0x01
+#define WMI_RX_STATUS_ERR_DECRYPT              0x08
+#define WMI_RX_STATUS_ERR_MIC                  0x10
+#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS       0x20
+
+struct wmi_single_phyerr_rx_hdr {
+       /* TSF timestamp */
+       __le32 tsf_timestamp;
+
+       /*
+        * Current freq1, freq2
+        *
+        * [7:0]:    freq1[lo]
+        * [15:8] :   freq1[hi]
+        * [23:16]:   freq2[lo]
+        * [31:24]:   freq2[hi]
+        */
+       __le16 freq1;
+       __le16 freq2;
+
+       /*
+        * Combined RSSI over all chains and channel width for this PHY error
+        *
+        * [7:0]: RSSI combined
+        * [15:8]: Channel width (MHz)
+        * [23:16]: PHY error code
+        * [24:16]: reserved (future use)
+        */
+       u8 rssi_combined;
+       u8 chan_width_mhz;
+       u8 phy_err_code;
+       u8 rsvd0;
+
+       /*
+        * RSSI on chain 0 through 3
+        *
+        * This is formatted the same as the PPDU_START RX descriptor
+        * field:
+        *
+        * [7:0]:   pri20
+        * [15:8]:  sec20
+        * [23:16]: sec40
+        * [31:24]: sec80
+        */
+
+       __le32 rssi_chain0;
+       __le32 rssi_chain1;
+       __le32 rssi_chain2;
+       __le32 rssi_chain3;
+
+       /*
+        * Last calibrated NF value for chain 0 through 3
+        *
+        * nf_list_1:
+        *
+        * + [15:0] - chain 0
+        * + [31:16] - chain 1
+        *
+        * nf_list_2:
+        *
+        * + [15:0] - chain 2
+        * + [31:16] - chain 3
+        */
+       __le32 nf_list_1;
+       __le32 nf_list_2;
+
+
+       /* Length of the frame */
+       __le32 buf_len;
+} __packed;
+
+struct wmi_single_phyerr_rx_event {
+       /* Phy error event header */
+       struct wmi_single_phyerr_rx_hdr hdr;
+       /* frame buffer */
+       u8 bufp[0];
+} __packed;
+
+struct wmi_comb_phyerr_rx_hdr {
+       /* Phy error phy error count */
+       __le32 num_phyerr_events;
+       __le32 tsf_l32;
+       __le32 tsf_u32;
+} __packed;
+
+struct wmi_comb_phyerr_rx_event {
+       /* Phy error phy error count */
+       struct wmi_comb_phyerr_rx_hdr hdr;
+       /*
+        * frame buffer - contains multiple payloads in the order:
+        *                    header - payload, header - payload...
+        *  (The header is of type: wmi_single_phyerr_rx_hdr)
+        */
+       u8 bufp[0];
+} __packed;
+
+struct wmi_mgmt_tx_hdr {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 tx_rate;
+       __le32 tx_power;
+       __le32 buf_len;
+} __packed;
+
+struct wmi_mgmt_tx_cmd {
+       struct wmi_mgmt_tx_hdr hdr;
+       u8 buf[0];
+} __packed;
+
+struct wmi_echo_event {
+       __le32 value;
+} __packed;
+
+struct wmi_echo_cmd {
+       __le32 value;
+} __packed;
+
+
+struct wmi_pdev_set_regdomain_cmd {
+       __le32 reg_domain;
+       __le32 reg_domain_2G;
+       __le32 reg_domain_5G;
+       __le32 conformance_test_limit_2G;
+       __le32 conformance_test_limit_5G;
+} __packed;
+
+/* Command to set/unset chip in quiet mode */
+struct wmi_pdev_set_quiet_cmd {
+       /* period in TUs */
+       __le32 period;
+
+       /* duration in TUs */
+       __le32 duration;
+
+       /* offset in TUs */
+       __le32 next_start;
+
+       /* enable/disable */
+       __le32 enabled;
+} __packed;
+
+
+/*
+ * 802.11g protection mode.
+ */
+enum ath10k_protmode {
+       ATH10K_PROT_NONE     = 0,    /* no protection */
+       ATH10K_PROT_CTSONLY  = 1,    /* CTS to self */
+       ATH10K_PROT_RTSCTS   = 2,    /* RTS-CTS */
+};
+
+enum wmi_beacon_gen_mode {
+       WMI_BEACON_STAGGERED_MODE = 0,
+       WMI_BEACON_BURST_MODE = 1
+};
+
+enum wmi_csa_event_ies_present_flag {
+       WMI_CSA_IE_PRESENT = 0x00000001,
+       WMI_XCSA_IE_PRESENT = 0x00000002,
+       WMI_WBW_IE_PRESENT = 0x00000004,
+       WMI_CSWARP_IE_PRESENT = 0x00000008,
+};
+
+/* wmi CSA receive event from beacon frame */
+struct wmi_csa_event {
+       __le32 i_fc_dur;
+       /* Bit 0-15: FC */
+       /* Bit 16-31: DUR */
+       struct wmi_mac_addr i_addr1;
+       struct wmi_mac_addr i_addr2;
+       __le32 csa_ie[2];
+       __le32 xcsa_ie[2];
+       __le32 wb_ie[2];
+       __le32 cswarp_ie;
+       __le32 ies_present_flag; /* wmi_csa_event_ies_present_flag */
+} __packed;
+
+/* the definition of different PDEV parameters */
+#define PDEV_DEFAULT_STATS_UPDATE_PERIOD    500
+#define VDEV_DEFAULT_STATS_UPDATE_PERIOD    500
+#define PEER_DEFAULT_STATS_UPDATE_PERIOD    500
+
+enum wmi_pdev_param {
+       /* TX chian mask */
+       WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
+       /* RX chian mask */
+       WMI_PDEV_PARAM_RX_CHAIN_MASK,
+       /* TX power limit for 2G Radio */
+       WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
+       /* TX power limit for 5G Radio */
+       WMI_PDEV_PARAM_TXPOWER_LIMIT5G,
+       /* TX power scale */
+       WMI_PDEV_PARAM_TXPOWER_SCALE,
+       /* Beacon generation mode . 0: host, 1: target   */
+       WMI_PDEV_PARAM_BEACON_GEN_MODE,
+       /* Beacon generation mode . 0: staggered 1: bursted   */
+       WMI_PDEV_PARAM_BEACON_TX_MODE,
+       /*
+        * Resource manager off chan mode .
+        * 0: turn off off chan mode. 1: turn on offchan mode
+        */
+       WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
+       /*
+        * Protection mode:
+        * 0: no protection 1:use CTS-to-self 2: use RTS/CTS
+        */
+       WMI_PDEV_PARAM_PROTECTION_MODE,
+       /* Dynamic bandwidth 0: disable 1: enable */
+       WMI_PDEV_PARAM_DYNAMIC_BW,
+       /* Non aggregrate/ 11g sw retry threshold.0-disable */
+       WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
+       /* aggregrate sw retry threshold. 0-disable*/
+       WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
+       /* Station kickout threshold (non of consecutive failures).0-disable */
+       WMI_PDEV_PARAM_STA_KICKOUT_TH,
+       /* Aggerate size scaling configuration per AC */
+       WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING,
+       /* LTR enable */
+       WMI_PDEV_PARAM_LTR_ENABLE,
+       /* LTR latency for BE, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_BE,
+       /* LTR latency for BK, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_BK,
+       /* LTR latency for VI, in us */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_VI,
+       /* LTR latency for VO, in us  */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_VO,
+       /* LTR AC latency timeout, in ms */
+       WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
+       /* LTR platform latency override, in us */
+       WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
+       /* LTR-RX override, in us */
+       WMI_PDEV_PARAM_LTR_RX_OVERRIDE,
+       /* Tx activity timeout for LTR, in us */
+       WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
+       /* L1SS state machine enable */
+       WMI_PDEV_PARAM_L1SS_ENABLE,
+       /* Deep sleep state machine enable */
+       WMI_PDEV_PARAM_DSLEEP_ENABLE,
+       /* RX buffering flush enable */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH,
+       /* RX buffering matermark */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK,
+       /* RX buffering timeout enable */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN,
+       /* RX buffering timeout value */
+       WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE,
+       /* pdev level stats update period in ms */
+       WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
+       /* vdev level stats update period in ms */
+       WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
+       /* peer level stats update period in ms */
+       WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
+       /* beacon filter status update period */
+       WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
+       /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */
+       WMI_PDEV_PARAM_PMF_QOS,
+       /* Access category on which ARP frames are sent */
+       WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+       /* DCS configuration */
+       WMI_PDEV_PARAM_DCS,
+       /* Enable/Disable ANI on target */
+       WMI_PDEV_PARAM_ANI_ENABLE,
+       /* configure the ANI polling period */
+       WMI_PDEV_PARAM_ANI_POLL_PERIOD,
+       /* configure the ANI listening period */
+       WMI_PDEV_PARAM_ANI_LISTEN_PERIOD,
+       /* configure OFDM immunity level */
+       WMI_PDEV_PARAM_ANI_OFDM_LEVEL,
+       /* configure CCK immunity level */
+       WMI_PDEV_PARAM_ANI_CCK_LEVEL,
+       /* Enable/Disable CDD for 1x1 STAs in rate control module */
+       WMI_PDEV_PARAM_DYNTXCHAIN,
+       /* Enable/Disable proxy STA */
+       WMI_PDEV_PARAM_PROXY_STA,
+       /* Enable/Disable low power state when all VDEVs are inactive/idle. */
+       WMI_PDEV_PARAM_IDLE_PS_CONFIG,
+       /* Enable/Disable power gating sleep */
+       WMI_PDEV_PARAM_POWER_GATING_SLEEP,
+};
+
+struct wmi_pdev_set_param_cmd {
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+struct wmi_pdev_get_tpc_config_cmd {
+       /* parameter   */
+       __le32 param;
+} __packed;
+
+#define WMI_TPC_RATE_MAX               160
+#define WMI_TPC_TX_N_CHAIN             4
+
+enum wmi_tpc_config_event_flag {
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD     = 0x1,
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC    = 0x2,
+       WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF    = 0x4,
+};
+
+struct wmi_pdev_tpc_config_event {
+       __le32 reg_domain;
+       __le32 chan_freq;
+       __le32 phy_mode;
+       __le32 twice_antenna_reduction;
+       __le32 twice_max_rd_power;
+       s32 twice_antenna_gain;
+       __le32 power_limit;
+       __le32 rate_max;
+       __le32 num_tx_chain;
+       __le32 ctl;
+       __le32 flags;
+       s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN];
+       u8 rates_array[WMI_TPC_RATE_MAX];
+} __packed;
+
+/* Transmit power scale factor. */
+enum wmi_tp_scale {
+       WMI_TP_SCALE_MAX    = 0,        /* no scaling (default) */
+       WMI_TP_SCALE_50     = 1,        /* 50% of max (-3 dBm) */
+       WMI_TP_SCALE_25     = 2,        /* 25% of max (-6 dBm) */
+       WMI_TP_SCALE_12     = 3,        /* 12% of max (-9 dBm) */
+       WMI_TP_SCALE_MIN    = 4,        /* min, but still on   */
+       WMI_TP_SCALE_SIZE   = 5,        /* max num of enum     */
+};
+
+struct wmi_set_channel_cmd {
+       /* channel (only frequency and mode info are used) */
+       struct wmi_channel chan;
+} __packed;
+
+struct wmi_pdev_chanlist_update_event {
+       /* number of channels */
+       __le32 num_chan;
+       /* array of channels */
+       struct wmi_channel channel_list[1];
+} __packed;
+
+#define WMI_MAX_DEBUG_MESG (sizeof(u32) * 32)
+
+struct wmi_debug_mesg_event {
+       /* message buffer, NULL terminated */
+       char bufp[WMI_MAX_DEBUG_MESG];
+} __packed;
+
+enum {
+       /* P2P device */
+       VDEV_SUBTYPE_P2PDEV = 0,
+       /* P2P client */
+       VDEV_SUBTYPE_P2PCLI,
+       /* P2P GO */
+       VDEV_SUBTYPE_P2PGO,
+       /* BT3.0 HS */
+       VDEV_SUBTYPE_BT,
+};
+
+struct wmi_pdev_set_channel_cmd {
+       /* idnore power , only use flags , mode and freq */
+       struct wmi_channel chan;
+} __packed;
+
+/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */
+#define WMI_DSCP_MAP_MAX    (64)
+struct wmi_pdev_set_dscp_tid_map_cmd {
+       /* map indicating DSCP to TID conversion */
+       __le32 dscp_to_tid_map[WMI_DSCP_MAP_MAX];
+} __packed;
+
+enum mcast_bcast_rate_id {
+       WMI_SET_MCAST_RATE,
+       WMI_SET_BCAST_RATE
+};
+
+struct mcast_bcast_rate {
+       enum mcast_bcast_rate_id rate_id;
+       __le32 rate;
+} __packed;
+
+struct wmi_wmm_params {
+       __le32 cwmin;
+       __le32 cwmax;
+       __le32 aifs;
+       __le32 txop;
+       __le32 acm;
+       __le32 no_ack;
+} __packed;
+
+struct wmi_pdev_set_wmm_params {
+       struct wmi_wmm_params ac_be;
+       struct wmi_wmm_params ac_bk;
+       struct wmi_wmm_params ac_vi;
+       struct wmi_wmm_params ac_vo;
+} __packed;
+
+struct wmi_wmm_params_arg {
+       u32 cwmin;
+       u32 cwmax;
+       u32 aifs;
+       u32 txop;
+       u32 acm;
+       u32 no_ack;
+};
+
+struct wmi_pdev_set_wmm_params_arg {
+       struct wmi_wmm_params_arg ac_be;
+       struct wmi_wmm_params_arg ac_bk;
+       struct wmi_wmm_params_arg ac_vi;
+       struct wmi_wmm_params_arg ac_vo;
+};
+
+struct wal_dbg_tx_stats {
+       /* Num HTT cookies queued to dispatch list */
+       __le32 comp_queued;
+
+       /* Num HTT cookies dispatched */
+       __le32 comp_delivered;
+
+       /* Num MSDU queued to WAL */
+       __le32 msdu_enqued;
+
+       /* Num MPDU queue to WAL */
+       __le32 mpdu_enqued;
+
+       /* Num MSDUs dropped by WMM limit */
+       __le32 wmm_drop;
+
+       /* Num Local frames queued */
+       __le32 local_enqued;
+
+       /* Num Local frames done */
+       __le32 local_freed;
+
+       /* Num queued to HW */
+       __le32 hw_queued;
+
+       /* Num PPDU reaped from HW */
+       __le32 hw_reaped;
+
+       /* Num underruns */
+       __le32 underrun;
+
+       /* Num PPDUs cleaned up in TX abort */
+       __le32 tx_abort;
+
+       /* Num MPDUs requed by SW */
+       __le32 mpdus_requed;
+
+       /* excessive retries */
+       __le32 tx_ko;
+
+       /* data hw rate code */
+       __le32 data_rc;
+
+       /* Scheduler self triggers */
+       __le32 self_triggers;
+
+       /* frames dropped due to excessive sw retries */
+       __le32 sw_retry_failure;
+
+       /* illegal rate phy errors  */
+       __le32 illgl_rate_phy_err;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_cont_xretry;
+
+       /* wal pdev continous xretry */
+       __le32 pdev_tx_timeout;
+
+       /* wal pdev resets  */
+       __le32 pdev_resets;
+
+       __le32 phy_underrun;
+
+       /* MPDU is more than txop limit */
+       __le32 txop_ovf;
+} __packed;
+
+struct wal_dbg_rx_stats {
+       /* Cnts any change in ring routing mid-ppdu */
+       __le32 mid_ppdu_route_change;
+
+       /* Total number of statuses processed */
+       __le32 status_rcvd;
+
+       /* Extra frags on rings 0-3 */
+       __le32 r0_frags;
+       __le32 r1_frags;
+       __le32 r2_frags;
+       __le32 r3_frags;
+
+       /* MSDUs / MPDUs delivered to HTT */
+       __le32 htt_msdus;
+       __le32 htt_mpdus;
+
+       /* MSDUs / MPDUs delivered to local stack */
+       __le32 loc_msdus;
+       __le32 loc_mpdus;
+
+       /* AMSDUs that have more MSDUs than the status ring size */
+       __le32 oversize_amsdu;
+
+       /* Number of PHY errors */
+       __le32 phy_errs;
+
+       /* Number of PHY errors drops */
+       __le32 phy_err_drop;
+
+       /* Number of mpdu errors - FCS, MIC, ENC etc. */
+       __le32 mpdu_errs;
+} __packed;
+
+struct wal_dbg_peer_stats {
+       /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+       __le32 dummy;
+} __packed;
+
+struct wal_dbg_stats {
+       struct wal_dbg_tx_stats tx;
+       struct wal_dbg_rx_stats rx;
+       struct wal_dbg_peer_stats peer;
+} __packed;
+
+enum wmi_stats_id {
+       WMI_REQUEST_PEER_STAT   = 0x01,
+       WMI_REQUEST_AP_STAT     = 0x02
+};
+
+struct wmi_request_stats_cmd {
+       __le32 stats_id;
+
+       /*
+        * Space to add parameters like
+        * peer mac addr
+        */
+} __packed;
+
+/* Suspend option */
+enum {
+       /* suspend */
+       WMI_PDEV_SUSPEND,
+
+       /* suspend and disable all interrupts */
+       WMI_PDEV_SUSPEND_AND_DISABLE_INTR,
+};
+
+struct wmi_pdev_suspend_cmd {
+       /* suspend option sent to target */
+       __le32 suspend_opt;
+} __packed;
+
+struct wmi_stats_event {
+       __le32 stats_id; /* %WMI_REQUEST_ */
+       /*
+        * number of pdev stats event structures
+        * (wmi_pdev_stats) 0 or 1
+        */
+       __le32 num_pdev_stats;
+       /*
+        * number of vdev stats event structures
+        * (wmi_vdev_stats) 0 or max vdevs
+        */
+       __le32 num_vdev_stats;
+       /*
+        * number of peer stats event structures
+        * (wmi_peer_stats) 0 or max peers
+        */
+       __le32 num_peer_stats;
+       __le32 num_bcnflt_stats;
+       /*
+        * followed by
+        *   num_pdev_stats * size of(struct wmi_pdev_stats)
+        *   num_vdev_stats * size of(struct wmi_vdev_stats)
+        *   num_peer_stats * size of(struct wmi_peer_stats)
+        *
+        *  By having a zero sized array, the pointer to data area
+        *  becomes available without increasing the struct size
+        */
+       u8 data[0];
+} __packed;
+
+/*
+ * PDEV statistics
+ * TODO: add all PDEV stats here
+ */
+struct wmi_pdev_stats {
+       __le32 chan_nf;        /* Channel noise floor */
+       __le32 tx_frame_count; /* TX frame count */
+       __le32 rx_frame_count; /* RX frame count */
+       __le32 rx_clear_count; /* rx clear count */
+       __le32 cycle_count;    /* cycle count */
+       __le32 phy_err_count;  /* Phy error count */
+       __le32 chan_tx_pwr;    /* channel tx power */
+       struct wal_dbg_stats wal; /* WAL dbg stats */
+} __packed;
+
+/*
+ * VDEV statistics
+ * TODO: add all VDEV stats here
+ */
+struct wmi_vdev_stats {
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * peer statistics.
+ * TODO: add more stats
+ */
+struct wmi_peer_stats {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_rssi;
+       __le32 peer_tx_rate;
+} __packed;
+
+struct wmi_vdev_create_cmd {
+       __le32 vdev_id;
+       __le32 vdev_type;
+       __le32 vdev_subtype;
+       struct wmi_mac_addr vdev_macaddr;
+} __packed;
+
+enum wmi_vdev_type {
+       WMI_VDEV_TYPE_AP      = 1,
+       WMI_VDEV_TYPE_STA     = 2,
+       WMI_VDEV_TYPE_IBSS    = 3,
+       WMI_VDEV_TYPE_MONITOR = 4,
+};
+
+enum wmi_vdev_subtype {
+       WMI_VDEV_SUBTYPE_NONE       = 0,
+       WMI_VDEV_SUBTYPE_P2P_DEVICE = 1,
+       WMI_VDEV_SUBTYPE_P2P_CLIENT = 2,
+       WMI_VDEV_SUBTYPE_P2P_GO     = 3,
+};
+
+/* values for vdev_subtype */
+
+/* values for vdev_start_request flags */
+/*
+ * Indicates that AP VDEV uses hidden ssid. only valid for
+ *  AP/GO */
+#define WMI_VDEV_START_HIDDEN_SSID  (1<<0)
+/*
+ * Indicates if robust management frame/management frame
+ *  protection is enabled. For GO/AP vdevs, it indicates that
+ *  it may support station/client associations with RMF enabled.
+ *  For STA/client vdevs, it indicates that sta will
+ *  associate with AP with RMF enabled. */
+#define WMI_VDEV_START_PMF_ENABLED  (1<<1)
+
+struct wmi_p2p_noa_descriptor {
+       __le32 type_count; /* 255: continuous schedule, 0: reserved */
+       __le32 duration;  /* Absent period duration in micro seconds */
+       __le32 interval;   /* Absent period interval in micro seconds */
+       __le32 start_time; /* 32 bit tsf time when in starts */
+} __packed;
+
+struct wmi_vdev_start_request_cmd {
+       /* WMI channel */
+       struct wmi_channel chan;
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* requestor id identifying the caller module */
+       __le32 requestor_id;
+       /* beacon interval from received beacon */
+       __le32 beacon_interval;
+       /* DTIM Period from the received beacon */
+       __le32 dtim_period;
+       /* Flags */
+       __le32 flags;
+       /* ssid field. Only valid for AP/GO/IBSS/BTAmp VDEV type. */
+       struct wmi_ssid ssid;
+       /* beacon/probe reponse xmit rate. Applicable for SoftAP. */
+       __le32 bcn_tx_rate;
+       /* beacon/probe reponse xmit power. Applicable for SoftAP. */
+       __le32 bcn_tx_power;
+       /* number of p2p NOA descriptor(s) from scan entry */
+       __le32 num_noa_descriptors;
+       /*
+        * Disable H/W ack. This used by WMI_VDEV_RESTART_REQUEST_CMDID.
+        * During CAC, Our HW shouldn't ack ditected frames
+        */
+       __le32 disable_hw_ack;
+       /* actual p2p NOA descriptor from scan entry */
+       struct wmi_p2p_noa_descriptor noa_descriptors[2];
+} __packed;
+
+struct wmi_vdev_restart_request_cmd {
+       struct wmi_vdev_start_request_cmd vdev_start_request_cmd;
+} __packed;
+
+struct wmi_vdev_start_request_arg {
+       u32 vdev_id;
+       struct wmi_channel_arg channel;
+       u32 bcn_intval;
+       u32 dtim_period;
+       u8 *ssid;
+       u32 ssid_len;
+       u32 bcn_tx_rate;
+       u32 bcn_tx_power;
+       bool disable_hw_ack;
+       bool hidden_ssid;
+       bool pmf_enabled;
+};
+
+struct wmi_vdev_delete_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_up_cmd {
+       __le32 vdev_id;
+       __le32 vdev_assoc_id;
+       struct wmi_mac_addr vdev_bssid;
+} __packed;
+
+struct wmi_vdev_stop_cmd {
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_down_cmd {
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_standby_response_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_resume_response_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_set_param_cmd {
+       __le32 vdev_id;
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+#define WMI_MAX_KEY_INDEX   3
+#define WMI_MAX_KEY_LEN     32
+
+#define WMI_KEY_PAIRWISE 0x00
+#define WMI_KEY_GROUP    0x01
+#define WMI_KEY_TX_USAGE 0x02 /* default tx key - static wep */
+
+struct wmi_key_seq_counter {
+       __le32 key_seq_counter_l;
+       __le32 key_seq_counter_h;
+} __packed;
+
+#define WMI_CIPHER_NONE     0x0 /* clear key */
+#define WMI_CIPHER_WEP      0x1
+#define WMI_CIPHER_TKIP     0x2
+#define WMI_CIPHER_AES_OCB  0x3
+#define WMI_CIPHER_AES_CCM  0x4
+#define WMI_CIPHER_WAPI     0x5
+#define WMI_CIPHER_CKIP     0x6
+#define WMI_CIPHER_AES_CMAC 0x7
+
+struct wmi_vdev_install_key_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 key_idx;
+       __le32 key_flags;
+       __le32 key_cipher; /* %WMI_CIPHER_ */
+       struct wmi_key_seq_counter key_rsc_counter;
+       struct wmi_key_seq_counter key_global_rsc_counter;
+       struct wmi_key_seq_counter key_tsc_counter;
+       u8 wpi_key_rsc_counter[16];
+       u8 wpi_key_tsc_counter[16];
+       __le32 key_len;
+       __le32 key_txmic_len;
+       __le32 key_rxmic_len;
+
+       /* contains key followed by tx mic followed by rx mic */
+       u8 key_data[0];
+} __packed;
+
+struct wmi_vdev_install_key_arg {
+       u32 vdev_id;
+       const u8 *macaddr;
+       u32 key_idx;
+       u32 key_flags;
+       u32 key_cipher;
+       u32 key_len;
+       u32 key_txmic_len;
+       u32 key_rxmic_len;
+       const void *key_data;
+};
+
+/* Preamble types to be used with VDEV fixed rate configuration */
+enum wmi_rate_preamble {
+       WMI_RATE_PREAMBLE_OFDM,
+       WMI_RATE_PREAMBLE_CCK,
+       WMI_RATE_PREAMBLE_HT,
+       WMI_RATE_PREAMBLE_VHT,
+};
+
+/* Value to disable fixed rate setting */
+#define WMI_FIXED_RATE_NONE    (0xff)
+
+/* the definition of different VDEV parameters */
+enum wmi_vdev_param {
+       /* RTS Threshold */
+       WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1,
+       /* Fragmentation threshold */
+       WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+       /* beacon interval in TUs */
+       WMI_VDEV_PARAM_BEACON_INTERVAL,
+       /* Listen interval in TUs */
+       WMI_VDEV_PARAM_LISTEN_INTERVAL,
+       /* muticast rate in Mbps */
+       WMI_VDEV_PARAM_MULTICAST_RATE,
+       /* management frame rate in Mbps */
+       WMI_VDEV_PARAM_MGMT_TX_RATE,
+       /* slot time (long vs short) */
+       WMI_VDEV_PARAM_SLOT_TIME,
+       /* preamble (long vs short) */
+       WMI_VDEV_PARAM_PREAMBLE,
+       /* SWBA time (time before tbtt in msec) */
+       WMI_VDEV_PARAM_SWBA_TIME,
+       /* time period for updating VDEV stats */
+       WMI_VDEV_STATS_UPDATE_PERIOD,
+       /* age out time in msec for frames queued for station in power save */
+       WMI_VDEV_PWRSAVE_AGEOUT_TIME,
+       /*
+        * Host SWBA interval (time in msec before tbtt for SWBA event
+        * generation).
+        */
+       WMI_VDEV_HOST_SWBA_INTERVAL,
+       /* DTIM period (specified in units of num beacon intervals) */
+       WMI_VDEV_PARAM_DTIM_PERIOD,
+       /*
+        * scheduler air time limit for this VDEV. used by off chan
+        * scheduler.
+        */
+       WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
+       /* enable/dsiable WDS for this VDEV  */
+       WMI_VDEV_PARAM_WDS,
+       /* ATIM Window */
+       WMI_VDEV_PARAM_ATIM_WINDOW,
+       /* BMISS max */
+       WMI_VDEV_PARAM_BMISS_COUNT_MAX,
+       /* BMISS first time */
+       WMI_VDEV_PARAM_BMISS_FIRST_BCNT,
+       /* BMISS final time */
+       WMI_VDEV_PARAM_BMISS_FINAL_BCNT,
+       /* WMM enables/disabled */
+       WMI_VDEV_PARAM_FEATURE_WMM,
+       /* Channel width */
+       WMI_VDEV_PARAM_CHWIDTH,
+       /* Channel Offset */
+       WMI_VDEV_PARAM_CHEXTOFFSET,
+       /* Disable HT Protection */
+       WMI_VDEV_PARAM_DISABLE_HTPROTECTION,
+       /* Quick STA Kickout */
+       WMI_VDEV_PARAM_STA_QUICKKICKOUT,
+       /* Rate to be used with Management frames */
+       WMI_VDEV_PARAM_MGMT_RATE,
+       /* Protection Mode */
+       WMI_VDEV_PARAM_PROTECTION_MODE,
+       /* Fixed rate setting */
+       WMI_VDEV_PARAM_FIXED_RATE,
+       /* Short GI Enable/Disable */
+       WMI_VDEV_PARAM_SGI,
+       /* Enable LDPC */
+       WMI_VDEV_PARAM_LDPC,
+       /* Enable Tx STBC */
+       WMI_VDEV_PARAM_TX_STBC,
+       /* Enable Rx STBC */
+       WMI_VDEV_PARAM_RX_STBC,
+       /* Intra BSS forwarding  */
+       WMI_VDEV_PARAM_INTRA_BSS_FWD,
+       /* Setting Default xmit key for Vdev */
+       WMI_VDEV_PARAM_DEF_KEYID,
+       /* NSS width */
+       WMI_VDEV_PARAM_NSS,
+       /* Set the custom rate for the broadcast data frames */
+       WMI_VDEV_PARAM_BCAST_DATA_RATE,
+       /* Set the custom rate (rate-code) for multicast data frames */
+       WMI_VDEV_PARAM_MCAST_DATA_RATE,
+       /* Tx multicast packet indicate Enable/Disable */
+       WMI_VDEV_PARAM_MCAST_INDICATE,
+       /* Tx DHCP packet indicate Enable/Disable */
+       WMI_VDEV_PARAM_DHCP_INDICATE,
+       /* Enable host inspection of Tx unicast packet to unknown destination */
+       WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
+
+       /* The minimum amount of time AP begins to consider STA inactive */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered inactive when there is no recent
+        * TX/RX activity and no downlink frames are buffered for it. Once a
+        * STA exceeds the maximum idle inactive time, the AP will send an
+        * 802.11 data-null as a keep alive to verify the STA is still
+        * associated. If the STA does ACK the data-null, or if the data-null
+        * is buffered and the STA does not retrieve it, the STA will be
+        * considered unresponsive
+        * (see WMI_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS).
+        */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
+
+       /*
+        * An associated STA is considered unresponsive if there is no recent
+        * TX/RX activity and downlink frames are buffered for it. Once a STA
+        * exceeds the maximum unresponsive time, the AP will send a
+        * WMI_STA_KICKOUT event to the host so the STA can be deleted. */
+       WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
+
+       /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */
+       WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
+       /* Enable/Disable RTS-CTS */
+       WMI_VDEV_PARAM_ENABLE_RTSCTS,
+       /* Enable TXBFee/er */
+       WMI_VDEV_PARAM_TXBF,
+
+       /* Set packet power save */
+       WMI_VDEV_PARAM_PACKET_POWERSAVE,
+
+       /*
+        * Drops un-encrypted packets if eceived in an encrypted connection
+        * otherwise forwards to host.
+        */
+       WMI_VDEV_PARAM_DROP_UNENCRY,
+
+       /*
+        * Set the encapsulation type for frames.
+        */
+       WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+};
+
+/* slot time long */
+#define WMI_VDEV_SLOT_TIME_LONG                0x1
+/* slot time short */
+#define WMI_VDEV_SLOT_TIME_SHORT       0x2
+/* preablbe long */
+#define WMI_VDEV_PREAMBLE_LONG         0x1
+/* preablbe short */
+#define WMI_VDEV_PREAMBLE_SHORT                0x2
+
+enum wmi_start_event_param {
+       WMI_VDEV_RESP_START_EVENT = 0,
+       WMI_VDEV_RESP_RESTART_EVENT,
+};
+
+struct wmi_vdev_start_response_event {
+       __le32 vdev_id;
+       __le32 req_id;
+       __le32 resp_type; /* %WMI_VDEV_RESP_ */
+       __le32 status;
+} __packed;
+
+struct wmi_vdev_standby_req_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_resume_req_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+struct wmi_vdev_stopped_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+/*
+ * common structure used for simple events
+ * (stopped, resume_req, standby response)
+ */
+struct wmi_vdev_simple_event {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+} __packed;
+
+/* VDEV start response status codes */
+/* VDEV succesfully started */
+#define WMI_INIFIED_VDEV_START_RESPONSE_STATUS_SUCCESS 0x0
+
+/* requested VDEV not found */
+#define WMI_INIFIED_VDEV_START_RESPONSE_INVALID_VDEVID 0x1
+
+/* unsupported VDEV combination */
+#define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED  0x2
+
+/* Beacon processing related command and event structures */
+struct wmi_bcn_tx_hdr {
+       __le32 vdev_id;
+       __le32 tx_rate;
+       __le32 tx_power;
+       __le32 bcn_len;
+} __packed;
+
+struct wmi_bcn_tx_cmd {
+       struct wmi_bcn_tx_hdr hdr;
+       u8 *bcn[0];
+} __packed;
+
+struct wmi_bcn_tx_arg {
+       u32 vdev_id;
+       u32 tx_rate;
+       u32 tx_power;
+       u32 bcn_len;
+       const void *bcn;
+};
+
+/* Beacon filter */
+#define WMI_BCN_FILTER_ALL   0 /* Filter all beacons */
+#define WMI_BCN_FILTER_NONE  1 /* Pass all beacons */
+#define WMI_BCN_FILTER_RSSI  2 /* Pass Beacons RSSI >= RSSI threshold */
+#define WMI_BCN_FILTER_BSSID 3 /* Pass Beacons with matching BSSID */
+#define WMI_BCN_FILTER_SSID  4 /* Pass Beacons with matching SSID */
+
+struct wmi_bcn_filter_rx_cmd {
+       /* Filter ID */
+       __le32 bcn_filter_id;
+       /* Filter type - wmi_bcn_filter */
+       __le32 bcn_filter;
+       /* Buffer len */
+       __le32 bcn_filter_len;
+       /* Filter info (threshold, BSSID, RSSI) */
+       u8 *bcn_filter_buf;
+} __packed;
+
+/* Capabilities and IEs to be passed to firmware */
+struct wmi_bcn_prb_info {
+       /* Capabilities */
+       __le32 caps;
+       /* ERP info */
+       __le32 erp;
+       /* Advanced capabilities */
+       /* HT capabilities */
+       /* HT Info */
+       /* ibss_dfs */
+       /* wpa Info */
+       /* rsn Info */
+       /* rrm info */
+       /* ath_ext */
+       /* app IE */
+} __packed;
+
+struct wmi_bcn_tmpl_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* TIM IE offset from the beginning of the template. */
+       __le32 tim_ie_offset;
+       /* beacon probe capabilities and IEs */
+       struct wmi_bcn_prb_info bcn_prb_info;
+       /* beacon buffer length */
+       __le32 buf_len;
+       /* variable length data */
+       u8 data[1];
+} __packed;
+
+struct wmi_prb_tmpl_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* beacon probe capabilities and IEs */
+       struct wmi_bcn_prb_info bcn_prb_info;
+       /* beacon buffer length */
+       __le32 buf_len;
+       /* Variable length data */
+       u8 data[1];
+} __packed;
+
+enum wmi_sta_ps_mode {
+       /* enable power save for the given STA VDEV */
+       WMI_STA_PS_MODE_DISABLED = 0,
+       /* disable power save  for a given STA VDEV */
+       WMI_STA_PS_MODE_ENABLED = 1,
+};
+
+struct wmi_sta_powersave_mode_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+
+       /*
+        * Power save mode
+        * (see enum wmi_sta_ps_mode)
+        */
+       __le32 sta_ps_mode;
+} __packed;
+
+enum wmi_csa_offload_en {
+       WMI_CSA_OFFLOAD_DISABLE = 0,
+       WMI_CSA_OFFLOAD_ENABLE = 1,
+};
+
+struct wmi_csa_offload_enable_cmd {
+       __le32 vdev_id;
+       __le32 csa_offload_enable;
+} __packed;
+
+struct wmi_csa_offload_chanswitch_cmd {
+       __le32 vdev_id;
+       struct wmi_channel chan;
+} __packed;
+
+/*
+ * This parameter controls the policy for retrieving frames from AP while the
+ * STA is in sleep state.
+ *
+ * Only takes affect if the sta_ps_mode is enabled
+ */
+enum wmi_sta_ps_param_rx_wake_policy {
+       /*
+        * Wake up when ever there is an  RX activity on the VDEV. In this mode
+        * the Power save SM(state machine) will come out of sleep by either
+        * sending null frame (or) a data frame (with PS==0) in response to TIM
+        * bit set in the received beacon frame from AP.
+        */
+       WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0,
+
+       /*
+        * Here the power save state machine will not wakeup in response to TIM
+        * bit, instead it will send a PSPOLL (or) UASPD trigger based on UAPSD
+        * configuration setup by WMISET_PS_SET_UAPSD  WMI command.  When all
+        * access categories are delivery-enabled, the station will send a
+        * UAPSD trigger frame, otherwise it will send a PS-Poll.
+        */
+       WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1,
+};
+
+/*
+ * Number of tx frames/beacon  that cause the power save SM to wake up.
+ *
+ * Value 1 causes the SM to wake up for every TX. Value 0 has a special
+ * meaning, It will cause the SM to never wake up. This is useful if you want
+ * to keep the system to sleep all the time for some kind of test mode . host
+ * can change this parameter any time.  It will affect at the next tx frame.
+ */
+enum wmi_sta_ps_param_tx_wake_threshold {
+       WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0,
+       WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1,
+
+       /*
+        * Values greater than one indicate that many TX attempts per beacon
+        * interval before the STA will wake up
+        */
+};
+
+/*
+ * The maximum number of PS-Poll frames the FW will send in response to
+ * traffic advertised in TIM before waking up (by sending a null frame with PS
+ * = 0). Value 0 has a special meaning: there is no maximum count and the FW
+ * will send as many PS-Poll as are necessary to retrieve buffered BU. This
+ * parameter is used when the RX wake policy is
+ * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake
+ * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE.
+ */
+enum wmi_sta_ps_param_pspoll_count {
+       WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0,
+       /*
+        * Values greater than 0 indicate the maximum numer of PS-Poll frames
+        * FW will send before waking up.
+        */
+};
+
+/*
+ * This will include the delivery and trigger enabled state for every AC.
+ * This is the negotiated state with AP. The host MLME needs to set this based
+ * on AP capability and the state Set in the association request by the
+ * station MLME.Lower 8 bits of the value specify the UAPSD configuration.
+ */
+#define WMI_UAPSD_AC_TYPE_DELI 0
+#define WMI_UAPSD_AC_TYPE_TRIG 1
+
+#define WMI_UAPSD_AC_BIT_MASK(ac, type) \
+       ((type ==  WMI_UAPSD_AC_TYPE_DELI) ? (1<<(ac<<1)) : (1<<((ac<<1)+1)))
+
+enum wmi_sta_ps_param_uapsd {
+       WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0),
+       WMI_STA_PS_UAPSD_AC0_TRIGGER_EN  = (1 << 1),
+       WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2),
+       WMI_STA_PS_UAPSD_AC1_TRIGGER_EN  = (1 << 3),
+       WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4),
+       WMI_STA_PS_UAPSD_AC2_TRIGGER_EN  = (1 << 5),
+       WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6),
+       WMI_STA_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
+};
+
+enum wmi_sta_powersave_param {
+       /*
+        * Controls how frames are retrievd from AP while STA is sleeping
+        *
+        * (see enum wmi_sta_ps_param_rx_wake_policy)
+        */
+       WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0,
+
+       /*
+        * The STA will go active after this many TX
+        *
+        * (see enum wmi_sta_ps_param_tx_wake_threshold)
+        */
+       WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1,
+
+       /*
+        * Number of PS-Poll to send before STA wakes up
+        *
+        * (see enum wmi_sta_ps_param_pspoll_count)
+        *
+        */
+       WMI_STA_PS_PARAM_PSPOLL_COUNT = 2,
+
+       /*
+        * TX/RX inactivity time in msec before going to sleep.
+        *
+        * The power save SM will monitor tx/rx activity on the VDEV, if no
+        * activity for the specified msec of the parameter the Power save
+        * SM will go to sleep.
+        */
+       WMI_STA_PS_PARAM_INACTIVITY_TIME = 3,
+
+       /*
+        * Set uapsd configuration.
+        *
+        * (see enum wmi_sta_ps_param_uapsd)
+        */
+       WMI_STA_PS_PARAM_UAPSD = 4,
+};
+
+struct wmi_sta_powersave_param_cmd {
+       __le32 vdev_id;
+       __le32 param_id; /* %WMI_STA_PS_PARAM_ */
+       __le32 param_value;
+} __packed;
+
+/* No MIMO power save */
+#define WMI_STA_MIMO_PS_MODE_DISABLE
+/* mimo powersave mode static*/
+#define WMI_STA_MIMO_PS_MODE_STATIC
+/* mimo powersave mode dynamic */
+#define WMI_STA_MIMO_PS_MODE_DYNAMIC
+
+struct wmi_sta_mimo_ps_mode_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* mimo powersave mode as defined above */
+       __le32 mimo_pwrsave_mode;
+} __packed;
+
+/* U-APSD configuration of peer station from (re)assoc request and TSPECs */
+enum wmi_ap_ps_param_uapsd {
+       WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0),
+       WMI_AP_PS_UAPSD_AC0_TRIGGER_EN  = (1 << 1),
+       WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2),
+       WMI_AP_PS_UAPSD_AC1_TRIGGER_EN  = (1 << 3),
+       WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4),
+       WMI_AP_PS_UAPSD_AC2_TRIGGER_EN  = (1 << 5),
+       WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6),
+       WMI_AP_PS_UAPSD_AC3_TRIGGER_EN  = (1 << 7),
+};
+
+/* U-APSD maximum service period of peer station */
+enum wmi_ap_ps_peer_param_max_sp {
+       WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2,
+       WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3,
+       MAX_WMI_AP_PS_PEER_PARAM_MAX_SP,
+};
+
+/*
+ * AP power save parameter
+ * Set a power save specific parameter for a peer station
+ */
+enum wmi_ap_ps_peer_param {
+       /* Set uapsd configuration for a given peer.
+        *
+        * Include the delivery and trigger enabled state for every AC.
+        * The host  MLME needs to set this based on AP capability and stations
+        * request Set in the association request  received from the station.
+        *
+        * Lower 8 bits of the value specify the UAPSD configuration.
+        *
+        * (see enum wmi_ap_ps_param_uapsd)
+        * The default value is 0.
+        */
+       WMI_AP_PS_PEER_PARAM_UAPSD = 0,
+
+       /*
+        * Set the service period for a UAPSD capable station
+        *
+        * The service period from wme ie in the (re)assoc request frame.
+        *
+        * (see enum wmi_ap_ps_peer_param_max_sp)
+        */
+       WMI_AP_PS_PEER_PARAM_MAX_SP = 1,
+
+       /* Time in seconds for aging out buffered frames for STA in PS */
+       WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2,
+};
+
+struct wmi_ap_ps_peer_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+
+       /* AP powersave param (see enum wmi_ap_ps_peer_param) */
+       __le32 param_id;
+
+       /* AP powersave param value */
+       __le32 param_value;
+} __packed;
+
+/* 128 clients = 4 words */
+#define WMI_TIM_BITMAP_ARRAY_SIZE 4
+
+struct wmi_tim_info {
+       __le32 tim_len;
+       __le32 tim_mcast;
+       __le32 tim_bitmap[WMI_TIM_BITMAP_ARRAY_SIZE];
+       __le32 tim_changed;
+       __le32 tim_num_ps_pending;
+} __packed;
+
+/* Maximum number of NOA Descriptors supported */
+#define WMI_P2P_MAX_NOA_DESCRIPTORS 4
+#define WMI_P2P_OPPPS_ENABLE_BIT       BIT(0)
+#define WMI_P2P_OPPPS_CTWINDOW_OFFSET  1
+#define WMI_P2P_NOA_CHANGED_BIT        BIT(0)
+
+struct wmi_p2p_noa_info {
+       /* Bit 0 - Flag to indicate an update in NOA schedule
+          Bits 7-1 - Reserved */
+       u8 changed;
+       /* NOA index */
+       u8 index;
+       /* Bit 0 - Opp PS state of the AP
+          Bits 1-7 - Ctwindow in TUs */
+       u8 ctwindow_oppps;
+       /* Number of NOA descriptors */
+       u8 num_descriptors;
+
+       struct wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
+} __packed;
+
+struct wmi_bcn_info {
+       struct wmi_tim_info tim_info;
+       struct wmi_p2p_noa_info p2p_noa_info;
+} __packed;
+
+struct wmi_host_swba_event {
+       __le32 vdev_map;
+       struct wmi_bcn_info bcn_info[1];
+} __packed;
+
+#define WMI_MAX_AP_VDEV 16
+
+struct wmi_tbtt_offset_event {
+       __le32 vdev_map;
+       __le32 tbttoffset_list[WMI_MAX_AP_VDEV];
+} __packed;
+
+
+struct wmi_peer_create_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_peer_delete_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_peer_flush_tids_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_tid_bitmap;
+} __packed;
+
+struct wmi_fixed_rate {
+       /*
+        * rate mode . 0: disable fixed rate (auto rate)
+        *   1: legacy (non 11n) rate  specified as ieee rate 2*Mbps
+        *   2: ht20 11n rate  specified as mcs index
+        *   3: ht40 11n rate  specified as mcs index
+        */
+       __le32  rate_mode;
+       /*
+        * 4 rate values for 4 rate series. series 0 is stored in byte 0 (LSB)
+        * and series 3 is stored at byte 3 (MSB)
+        */
+       __le32  rate_series;
+       /*
+        * 4 retry counts for 4 rate series. retry count for rate 0 is stored
+        * in byte 0 (LSB) and retry count for rate 3 is stored at byte 3
+        * (MSB)
+        */
+       __le32  rate_retries;
+} __packed;
+
+struct wmi_peer_fixed_rate_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* fixed rate */
+       struct wmi_fixed_rate peer_fixed_rate;
+} __packed;
+
+#define WMI_MGMT_TID    17
+
+struct wmi_addba_clear_resp_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+struct wmi_addba_send_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* Buffer/Window size*/
+       __le32 buffersize;
+} __packed;
+
+struct wmi_delba_send_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* Is Initiator */
+       __le32 initiator;
+       /* Reason code */
+       __le32 reasoncode;
+} __packed;
+
+struct wmi_addba_setresponse_cmd {
+       /* unique id identifying the vdev, generated by the caller */
+       __le32 vdev_id;
+       /* peer mac address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+       /* status code */
+       __le32 statuscode;
+} __packed;
+
+struct wmi_send_singleamsdu_cmd {
+       /* unique id identifying the vdev, generated by the caller */
+       __le32 vdev_id;
+       /* peer mac address */
+       struct wmi_mac_addr peer_macaddr;
+       /* Tid number */
+       __le32 tid;
+} __packed;
+
+enum wmi_peer_smps_state {
+       WMI_PEER_SMPS_PS_NONE = 0x0,
+       WMI_PEER_SMPS_STATIC  = 0x1,
+       WMI_PEER_SMPS_DYNAMIC = 0x2
+};
+
+enum wmi_peer_param {
+       WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
+       WMI_PEER_AMPDU      = 0x2,
+       WMI_PEER_AUTHORIZE  = 0x3,
+       WMI_PEER_CHAN_WIDTH = 0x4,
+       WMI_PEER_NSS        = 0x5,
+       WMI_PEER_USE_4ADDR  = 0x6
+};
+
+struct wmi_peer_set_param_cmd {
+       __le32 vdev_id;
+       struct wmi_mac_addr peer_macaddr;
+       __le32 param_id;
+       __le32 param_value;
+} __packed;
+
+#define MAX_SUPPORTED_RATES 128
+
+struct wmi_rate_set {
+       /* total number of rates */
+       __le32 num_rates;
+       /*
+        * rates (each 8bit value) packed into a 32 bit word.
+        * the rates are filled from least significant byte to most
+        * significant byte.
+        */
+       __le32 rates[(MAX_SUPPORTED_RATES/4)+1];
+} __packed;
+
+struct wmi_rate_set_arg {
+       unsigned int num_rates;
+       u8 rates[MAX_SUPPORTED_RATES];
+};
+
+/*
+ * NOTE: It would bea good idea to represent the Tx MCS
+ * info in one word and Rx in another word. This is split
+ * into multiple words for convenience
+ */
+struct wmi_vht_rate_set {
+       __le32 rx_max_rate; /* Max Rx data rate */
+       __le32 rx_mcs_set;  /* Negotiated RX VHT rates */
+       __le32 tx_max_rate; /* Max Tx data rate */
+       __le32 tx_mcs_set;  /* Negotiated TX VHT rates */
+} __packed;
+
+struct wmi_vht_rate_set_arg {
+       u32 rx_max_rate;
+       u32 rx_mcs_set;
+       u32 tx_max_rate;
+       u32 tx_mcs_set;
+};
+
+struct wmi_peer_set_rates_cmd {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* legacy rate set */
+       struct wmi_rate_set peer_legacy_rates;
+       /* ht rate set */
+       struct wmi_rate_set peer_ht_rates;
+} __packed;
+
+struct wmi_peer_set_q_empty_callback_cmd {
+       /* unique id identifying the VDEV, generated by the caller */
+       __le32 vdev_id;
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       __le32 callback_enable;
+} __packed;
+
+#define WMI_PEER_AUTH           0x00000001
+#define WMI_PEER_QOS            0x00000002
+#define WMI_PEER_NEED_PTK_4_WAY 0x00000004
+#define WMI_PEER_NEED_GTK_2_WAY 0x00000010
+#define WMI_PEER_APSD           0x00000800
+#define WMI_PEER_HT             0x00001000
+#define WMI_PEER_40MHZ          0x00002000
+#define WMI_PEER_STBC           0x00008000
+#define WMI_PEER_LDPC           0x00010000
+#define WMI_PEER_DYN_MIMOPS     0x00020000
+#define WMI_PEER_STATIC_MIMOPS  0x00040000
+#define WMI_PEER_SPATIAL_MUX    0x00200000
+#define WMI_PEER_VHT            0x02000000
+#define WMI_PEER_80MHZ          0x04000000
+#define WMI_PEER_PMF            0x08000000
+
+/*
+ * Peer rate capabilities.
+ *
+ * This is of interest to the ratecontrol
+ * module which resides in the firmware. The bit definitions are
+ * consistent with that defined in if_athrate.c.
+ */
+#define WMI_RC_DS_FLAG          0x01
+#define WMI_RC_CW40_FLAG        0x02
+#define WMI_RC_SGI_FLAG         0x04
+#define WMI_RC_HT_FLAG          0x08
+#define WMI_RC_RTSCTS_FLAG      0x10
+#define WMI_RC_TX_STBC_FLAG     0x20
+#define WMI_RC_RX_STBC_FLAG     0xC0
+#define WMI_RC_RX_STBC_FLAG_S   6
+#define WMI_RC_WEP_TKIP_FLAG    0x100
+#define WMI_RC_TS_FLAG          0x200
+#define WMI_RC_UAPSD_FLAG       0x400
+
+/* Maximum listen interval supported by hw in units of beacon interval */
+#define ATH10K_MAX_HW_LISTEN_INTERVAL 5
+
+struct wmi_peer_assoc_complete_cmd {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 vdev_id;
+       __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */
+       __le32 peer_associd; /* 16 LSBs */
+       __le32 peer_flags;
+       __le32 peer_caps; /* 16 LSBs */
+       __le32 peer_listen_intval;
+       __le32 peer_ht_caps;
+       __le32 peer_max_mpdu;
+       __le32 peer_mpdu_density; /* 0..16 */
+       __le32 peer_rate_caps;
+       struct wmi_rate_set peer_legacy_rates;
+       struct wmi_rate_set peer_ht_rates;
+       __le32 peer_nss; /* num of spatial streams */
+       __le32 peer_vht_caps;
+       __le32 peer_phymode;
+       struct wmi_vht_rate_set peer_vht_rates;
+       /* HT Operation Element of the peer. Five bytes packed in 2
+        *  INT32 array and filled from lsb to msb. */
+       __le32 peer_ht_info[2];
+} __packed;
+
+struct wmi_peer_assoc_complete_arg {
+       u8 addr[ETH_ALEN];
+       u32 vdev_id;
+       bool peer_reassoc;
+       u16 peer_aid;
+       u32 peer_flags; /* see %WMI_PEER_ */
+       u16 peer_caps;
+       u32 peer_listen_intval;
+       u32 peer_ht_caps;
+       u32 peer_max_mpdu;
+       u32 peer_mpdu_density; /* 0..16 */
+       u32 peer_rate_caps; /* see %WMI_RC_ */
+       struct wmi_rate_set_arg peer_legacy_rates;
+       struct wmi_rate_set_arg peer_ht_rates;
+       u32 peer_num_spatial_streams;
+       u32 peer_vht_caps;
+       enum wmi_phy_mode peer_phymode;
+       struct wmi_vht_rate_set_arg peer_vht_rates;
+};
+
+struct wmi_peer_add_wds_entry_cmd {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+       /* wds MAC addr */
+       struct wmi_mac_addr wds_macaddr;
+} __packed;
+
+struct wmi_peer_remove_wds_entry_cmd {
+       /* wds MAC addr */
+       struct wmi_mac_addr wds_macaddr;
+} __packed;
+
+struct wmi_peer_q_empty_callback_event {
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
+/*
+ * Channel info WMI event
+ */
+struct wmi_chan_info_event {
+       __le32 err_code;
+       __le32 freq;
+       __le32 cmd_flags;
+       __le32 noise_floor;
+       __le32 rx_clear_count;
+       __le32 cycle_count;
+} __packed;
+
+/* Beacon filter wmi command info */
+#define BCN_FLT_MAX_SUPPORTED_IES      256
+#define BCN_FLT_MAX_ELEMS_IE_LIST      (BCN_FLT_MAX_SUPPORTED_IES / 32)
+
+struct bss_bcn_stats {
+       __le32 vdev_id;
+       __le32 bss_bcnsdropped;
+       __le32 bss_bcnsdelivered;
+} __packed;
+
+struct bcn_filter_stats {
+       __le32 bcns_dropped;
+       __le32 bcns_delivered;
+       __le32 activefilters;
+       struct bss_bcn_stats bss_stats;
+} __packed;
+
+struct wmi_add_bcn_filter_cmd {
+       u32 vdev_id;
+       u32 ie_map[BCN_FLT_MAX_ELEMS_IE_LIST];
+} __packed;
+
+enum wmi_sta_keepalive_method {
+       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1,
+       WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2,
+};
+
+/* note: ip4 addresses are in network byte order, i.e. big endian */
+struct wmi_sta_keepalive_arp_resp {
+       __be32 src_ip4_addr;
+       __be32 dest_ip4_addr;
+       struct wmi_mac_addr dest_mac_addr;
+} __packed;
+
+struct wmi_sta_keepalive_cmd {
+       __le32 vdev_id;
+       __le32 enabled;
+       __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */
+       __le32 interval; /* in seconds */
+       struct wmi_sta_keepalive_arp_resp arp_resp;
+} __packed;
+
+#define ATH10K_RTS_MAX         2347
+#define ATH10K_FRAGMT_THRESHOLD_MIN    540
+#define ATH10K_FRAGMT_THRESHOLD_MAX    2346
+
+#define WMI_MAX_EVENT 0x1000
+/* Maximum number of pending TXed WMI packets */
+#define WMI_MAX_PENDING_TX_COUNT 128
+#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr)
+
+/* By default disable power save for IBSS */
+#define ATH10K_DEFAULT_ATIM 0
+
+struct ath10k;
+struct ath10k_vif;
+
+int ath10k_wmi_attach(struct ath10k *ar);
+void ath10k_wmi_detach(struct ath10k *ar);
+int ath10k_wmi_wait_for_service_ready(struct ath10k *ar);
+int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
+void ath10k_wmi_flush_tx(struct ath10k *ar);
+
+int ath10k_wmi_connect_htc_service(struct ath10k *ar);
+int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
+                               const struct wmi_channel_arg *);
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
+int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g);
+int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id,
+                             u32 value);
+int ath10k_wmi_cmd_init(struct ath10k *ar);
+int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *);
+void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *);
+int ath10k_wmi_stop_scan(struct ath10k *ar,
+                        const struct wmi_stop_scan_arg *arg);
+int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
+                          enum wmi_vdev_type type,
+                          enum wmi_vdev_subtype subtype,
+                          const u8 macaddr[ETH_ALEN]);
+int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_start(struct ath10k *ar,
+                         const struct wmi_vdev_start_request_arg *);
+int ath10k_wmi_vdev_restart(struct ath10k *ar,
+                           const struct wmi_vdev_start_request_arg *);
+int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
+                      const u8 *bssid);
+int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id);
+int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
+                             enum wmi_vdev_param param_id, u32 param_value);
+int ath10k_wmi_vdev_install_key(struct ath10k *ar,
+                               const struct wmi_vdev_install_key_arg *arg);
+int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
+                   const u8 peer_addr[ETH_ALEN]);
+int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
+                   const u8 peer_addr[ETH_ALEN]);
+int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
+                  const u8 peer_addr[ETH_ALEN], u32 tid_bitmap);
+int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
+                             const u8 *peer_addr,
+                             enum wmi_peer_param param_id, u32 param_value);
+int ath10k_wmi_peer_assoc(struct ath10k *ar,
+                         const struct wmi_peer_assoc_complete_arg *arg);
+int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
+                         enum wmi_sta_ps_mode psmode);
+int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_sta_powersave_param param_id,
+                               u32 value);
+int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
+                              enum wmi_ap_ps_peer_param param_id, u32 value);
+int ath10k_wmi_scan_chan_list(struct ath10k *ar,
+                             const struct wmi_scan_chan_list_arg *arg);
+int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
+int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
+                       const struct wmi_pdev_set_wmm_params_arg *arg);
+int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
+
+#endif /* _WMI_H_ */
index 7f702fe..ce67ab7 100644 (file)
@@ -60,6 +60,7 @@
 
 #include <asm/unaligned.h>
 
+#include <net/mac80211.h>
 #include "base.h"
 #include "reg.h"
 #include "debug.h"
@@ -666,9 +667,46 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb)
        return htype;
 }
 
+static struct ieee80211_rate *
+ath5k_get_rate(const struct ieee80211_hw *hw,
+              const struct ieee80211_tx_info *info,
+              struct ath5k_buf *bf, int idx)
+{
+       /*
+       * convert a ieee80211_tx_rate RC-table entry to
+       * the respective ieee80211_rate struct
+       */
+       if (bf->rates[idx].idx < 0) {
+               return NULL;
+       }
+
+       return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ];
+}
+
+static u16
+ath5k_get_rate_hw_value(const struct ieee80211_hw *hw,
+                       const struct ieee80211_tx_info *info,
+                       struct ath5k_buf *bf, int idx)
+{
+       struct ieee80211_rate *rate;
+       u16 hw_rate;
+       u8 rc_flags;
+
+       rate = ath5k_get_rate(hw, info, bf, idx);
+       if (!rate)
+               return 0;
+
+       rc_flags = bf->rates[idx].flags;
+       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
+                  rate->hw_value_short : rate->hw_value;
+
+       return hw_rate;
+}
+
 static int
 ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
-                 struct ath5k_txq *txq, int padsize)
+                 struct ath5k_txq *txq, int padsize,
+                 struct ieee80211_tx_control *control)
 {
        struct ath5k_desc *ds = bf->desc;
        struct sk_buff *skb = bf->skb;
@@ -688,7 +726,11 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
        bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len,
                        DMA_TO_DEVICE);
 
-       rate = ieee80211_get_tx_rate(ah->hw, info);
+       ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates,
+                              ARRAY_SIZE(bf->rates));
+
+       rate = ath5k_get_rate(ah->hw, info, bf, 0);
+
        if (!rate) {
                ret = -EINVAL;
                goto err_unmap;
@@ -698,8 +740,8 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
                flags |= AR5K_TXDESC_NOACK;
 
        rc_flags = info->control.rates[0].flags;
-       hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ?
-               rate->hw_value_short : rate->hw_value;
+
+       hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0);
 
        pktlen = skb->len;
 
@@ -722,12 +764,13 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
                duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw,
                        info->control.vif, pktlen, info));
        }
+
        ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
                ieee80211_get_hdrlen_from_skb(skb), padsize,
                get_hw_packet_type(skb),
                (ah->ah_txpower.txp_requested * 2),
                hw_rate,
-               info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags,
+               bf->rates[0].count, keyidx, ah->ah_tx_ant, flags,
                cts_rate, duration);
        if (ret)
                goto err_unmap;
@@ -736,13 +779,15 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
        if (ah->ah_capabilities.cap_has_mrr_support) {
                memset(mrr_rate, 0, sizeof(mrr_rate));
                memset(mrr_tries, 0, sizeof(mrr_tries));
+
                for (i = 0; i < 3; i++) {
-                       rate = ieee80211_get_alt_retry_rate(ah->hw, info, i);
+
+                       rate = ath5k_get_rate(ah->hw, info, bf, i);
                        if (!rate)
                                break;
 
-                       mrr_rate[i] = rate->hw_value;
-                       mrr_tries[i] = info->control.rates[i + 1].count;
+                       mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i);
+                       mrr_tries[i] = bf->rates[i].count;
                }
 
                ath5k_hw_setup_mrr_tx_desc(ah, ds,
@@ -1515,7 +1560,7 @@ unlock:
 
 void
 ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-              struct ath5k_txq *txq)
+              struct ath5k_txq *txq, struct ieee80211_tx_control *control)
 {
        struct ath5k_hw *ah = hw->priv;
        struct ath5k_buf *bf;
@@ -1555,7 +1600,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
 
        bf->skb = skb;
 
-       if (ath5k_txbuf_setup(ah, bf, txq, padsize)) {
+       if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) {
                bf->skb = NULL;
                spin_lock_irqsave(&ah->txbuflock, flags);
                list_add_tail(&bf->list, &ah->txbuf);
@@ -1571,11 +1616,13 @@ drop_packet:
 
 static void
 ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
-                        struct ath5k_txq *txq, struct ath5k_tx_status *ts)
+                        struct ath5k_txq *txq, struct ath5k_tx_status *ts,
+                        struct ath5k_buf *bf)
 {
        struct ieee80211_tx_info *info;
        u8 tries[3];
        int i;
+       int size = 0;
 
        ah->stats.tx_all_count++;
        ah->stats.tx_bytes_count += skb->len;
@@ -1587,6 +1634,9 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
 
        ieee80211_tx_info_clear_status(info);
 
+       size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates));
+       memcpy(info->status.rates, bf->rates, size);
+
        for (i = 0; i < ts->ts_final_idx; i++) {
                struct ieee80211_tx_rate *r =
                        &info->status.rates[i];
@@ -1663,7 +1713,7 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq)
 
                        dma_unmap_single(ah->dev, bf->skbaddr, skb->len,
                                        DMA_TO_DEVICE);
-                       ath5k_tx_frame_completed(ah, skb, txq, &ts);
+                       ath5k_tx_frame_completed(ah, skb, txq, &ts, bf);
                }
 
                /*
@@ -1917,7 +1967,7 @@ ath5k_beacon_send(struct ath5k_hw *ah)
 
        skb = ieee80211_get_buffered_bc(ah->hw, vif);
        while (skb) {
-               ath5k_tx_queue(ah->hw, skb, ah->cabq);
+               ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL);
 
                if (ah->cabq->txq_len >= ah->cabq->txq_max)
                        break;
@@ -2442,7 +2492,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
                        IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                        IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_MFP_CAPABLE |
-                       IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+                       IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                       IEEE80211_HW_SUPPORTS_RC_TABLE;
 
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_AP) |
index 6c94c7f..ca9a83c 100644 (file)
@@ -47,6 +47,7 @@ struct ath5k_hw;
 struct ath5k_txq;
 struct ieee80211_channel;
 struct ath_bus_ops;
+struct ieee80211_tx_control;
 enum nl80211_iftype;
 
 enum ath5k_srev_type {
@@ -61,11 +62,12 @@ struct ath5k_srev_name {
 };
 
 struct ath5k_buf {
-       struct list_head        list;
-       struct ath5k_desc       *desc;  /* virtual addr of desc */
-       dma_addr_t              daddr;  /* physical addr of desc */
-       struct sk_buff          *skb;   /* skbuff for buf */
-       dma_addr_t              skbaddr;/* physical addr of skb data */
+       struct list_head                list;
+       struct ath5k_desc               *desc;          /* virtual addr of desc */
+       dma_addr_t                      daddr;          /* physical addr of desc */
+       struct sk_buff                  *skb;           /* skbuff for buf */
+       dma_addr_t                      skbaddr;        /* physical addr of skb data */
+       struct ieee80211_tx_rate        rates[4];       /* number of multi-rate stages */
 };
 
 struct ath5k_vif {
@@ -103,7 +105,7 @@ int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
 void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
-                   struct ath5k_txq *txq);
+                   struct ath5k_txq *txq, struct ieee80211_tx_control *control);
 
 const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
 
index 06f86f4..81b686c 100644 (file)
@@ -66,7 +66,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
                return;
        }
 
-       ath5k_tx_queue(hw, skb, &ah->txqs[qnum]);
+       ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control);
 }
 
 
index 5c9736a..2437ad2 100644 (file)
@@ -3175,10 +3175,21 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
        struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev);
        struct ath6kl *ar = ath6kl_priv(vif->ndev);
-       u32 id;
+       u32 id, freq;
        const struct ieee80211_mgmt *mgmt;
        bool more_data, queued;
 
+       /* default to the current channel, but use the one specified as argument
+        * if any
+        */
+       freq = vif->ch_hint;
+       if (chan)
+               freq = chan->center_freq;
+
+       /* never send freq zero to the firmware */
+       if (WARN_ON(freq == 0))
+               return -EINVAL;
+
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) &&
            ieee80211_is_probe_resp(mgmt->frame_control) &&
@@ -3188,8 +3199,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                 * command to allow the target to fill in the generic IEs.
                 */
                *cookie = 0; /* TX status not supported */
-               return ath6kl_send_go_probe_resp(vif, buf, len,
-                                                chan->center_freq);
+               return ath6kl_send_go_probe_resp(vif, buf, len, freq);
        }
 
        id = vif->send_action_id++;
@@ -3205,17 +3215,14 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        /* AP mode Power saving processing */
        if (vif->nw_type == AP_NETWORK) {
-               queued = ath6kl_mgmt_powersave_ap(vif,
-                                       id, chan->center_freq,
-                                       wait, buf,
-                                       len, &more_data, no_cck);
+               queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len,
+                                                 &more_data, no_cck);
                if (queued)
                        return 0;
        }
 
-       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
-                                       chan->center_freq, wait,
-                                       buf, len, no_cck);
+       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq,
+                                       wait, buf, len, no_cck);
 }
 
 static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
@@ -3679,6 +3686,20 @@ err:
        return NULL;
 }
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath6kl_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT |
+                WIPHY_WOWLAN_DISCONNECT |
+                WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
+                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
+                WIPHY_WOWLAN_4WAY_HANDSHAKE,
+       .n_patterns = WOW_MAX_FILTERS_PER_LIST,
+       .pattern_min_len = 1,
+       .pattern_max_len = WOW_PATTERN_SIZE,
+};
+#endif
+
 int ath6kl_cfg80211_init(struct ath6kl *ar)
 {
        struct wiphy *wiphy = ar->wiphy;
@@ -3772,15 +3793,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
        wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                             WIPHY_WOWLAN_DISCONNECT |
-                             WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
-                             WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                             WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
-                             WIPHY_WOWLAN_4WAY_HANDSHAKE;
-       wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
+       wiphy->wowlan = &ath6kl_wowlan_support;
 #endif
 
        wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
index fe38b83..dbfd17d 100644 (file)
@@ -1240,20 +1240,14 @@ static ssize_t ath6kl_force_roam_write(struct file *file,
        char buf[20];
        size_t len;
        u8 bssid[ETH_ALEN];
-       int i;
-       int addr[ETH_ALEN];
 
        len = min(count, sizeof(buf) - 1);
        if (copy_from_user(buf, user_buf, len))
                return -EFAULT;
        buf[len] = '\0';
 
-       if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
-                  &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5])
-           != ETH_ALEN)
+       if (!mac_pton(buf, bssid))
                return -EINVAL;
-       for (i = 0; i < ETH_ALEN; i++)
-               bssid[i] = addr[i];
 
        ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid);
        if (ret)
index 40ffee6..6a67881 100644 (file)
@@ -1696,10 +1696,16 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                                                    test_bit(WMI_READY,
                                                             &ar->flag),
                                                    WMI_TIMEOUT);
+       if (timeleft <= 0) {
+               clear_bit(WMI_READY, &ar->flag);
+               ath6kl_err("wmi is not ready or wait was interrupted: %ld\n",
+                          timeleft);
+               ret = -EIO;
+               goto err_htc_stop;
+       }
 
        ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
 
-
        if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
                ath6kl_info("%s %s fw %s api %d%s\n",
                            ar->hw.name,
@@ -1718,12 +1724,6 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar)
                goto err_htc_stop;
        }
 
-       if (!timeleft || signal_pending(current)) {
-               ath6kl_err("wmi is not ready or wait was interrupted\n");
-               ret = -EIO;
-               goto err_htc_stop;
-       }
-
        ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);
 
        /* communicate the wmi protocol verision to the target */
index fb14145..7126bdd 100644 (file)
@@ -345,17 +345,17 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
 {
        struct hif_scatter_req *s_req;
        struct bus_request *bus_req;
-       int i, scat_req_sz, scat_list_sz, sg_sz, buf_sz;
+       int i, scat_req_sz, scat_list_sz, size;
        u8 *virt_buf;
 
        scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
        scat_req_sz = sizeof(*s_req) + scat_list_sz;
 
        if (!virt_scat)
-               sg_sz = sizeof(struct scatterlist) * n_scat_entry;
+               size = sizeof(struct scatterlist) * n_scat_entry;
        else
-               buf_sz =  2 * L1_CACHE_BYTES +
-                         ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
+               size =  2 * L1_CACHE_BYTES +
+                       ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
 
        for (i = 0; i < n_scat_req; i++) {
                /* allocate the scatter request */
@@ -364,7 +364,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
                        return -ENOMEM;
 
                if (virt_scat) {
-                       virt_buf = kzalloc(buf_sz, GFP_KERNEL);
+                       virt_buf = kzalloc(size, GFP_KERNEL);
                        if (!virt_buf) {
                                kfree(s_req);
                                return -ENOMEM;
@@ -374,7 +374,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
                                (u8 *)L1_CACHE_ALIGN((unsigned long)virt_buf);
                } else {
                        /* allocate sglist */
-                       s_req->sgentries = kzalloc(sg_sz, GFP_KERNEL);
+                       s_req->sgentries = kzalloc(size, GFP_KERNEL);
 
                        if (!s_req->sgentries) {
                                kfree(s_req);
index bed0d33..f38ff6a 100644 (file)
@@ -1061,6 +1061,22 @@ static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar)
        return;
 }
 
+static int ath6kl_usb_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+       /*
+        * cfg80211 suspend/WOW currently not supported for USB.
+        */
+       return 0;
+}
+
+static int ath6kl_usb_resume(struct ath6kl *ar)
+{
+       /*
+        * cfg80211 resume currently not supported for USB.
+        */
+       return 0;
+}
+
 static const struct ath6kl_hif_ops ath6kl_usb_ops = {
        .diag_read32 = ath6kl_usb_diag_read32,
        .diag_write32 = ath6kl_usb_diag_write32,
@@ -1074,6 +1090,8 @@ static const struct ath6kl_hif_ops ath6kl_usb_ops = {
        .pipe_map_service = ath6kl_usb_map_service_pipe,
        .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number,
        .cleanup_scatter = ath6kl_usb_cleanup_scatter,
+       .suspend = ath6kl_usb_suspend,
+       .resume = ath6kl_usb_resume,
 };
 
 /* ath6kl usb driver registered functions */
@@ -1152,7 +1170,7 @@ static void ath6kl_usb_remove(struct usb_interface *interface)
 
 #ifdef CONFIG_PM
 
-static int ath6kl_usb_suspend(struct usb_interface *interface,
+static int ath6kl_usb_pm_suspend(struct usb_interface *interface,
                              pm_message_t message)
 {
        struct ath6kl_usb *device;
@@ -1162,7 +1180,7 @@ static int ath6kl_usb_suspend(struct usb_interface *interface,
        return 0;
 }
 
-static int ath6kl_usb_resume(struct usb_interface *interface)
+static int ath6kl_usb_pm_resume(struct usb_interface *interface)
 {
        struct ath6kl_usb *device;
        device = usb_get_intfdata(interface);
@@ -1175,7 +1193,7 @@ static int ath6kl_usb_resume(struct usb_interface *interface)
        return 0;
 }
 
-static int ath6kl_usb_reset_resume(struct usb_interface *intf)
+static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
 {
        if (usb_get_intfdata(intf))
                ath6kl_usb_remove(intf);
@@ -1184,9 +1202,9 @@ static int ath6kl_usb_reset_resume(struct usb_interface *intf)
 
 #else
 
-#define ath6kl_usb_suspend NULL
-#define ath6kl_usb_resume NULL
-#define ath6kl_usb_reset_resume NULL
+#define ath6kl_usb_pm_suspend NULL
+#define ath6kl_usb_pm_resume NULL
+#define ath6kl_usb_pm_reset_resume NULL
 
 #endif
 
@@ -1201,9 +1219,9 @@ MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids);
 static struct usb_driver ath6kl_usb_driver = {
        .name = "ath6kl_usb",
        .probe = ath6kl_usb_probe,
-       .suspend = ath6kl_usb_suspend,
-       .resume = ath6kl_usb_resume,
-       .reset_resume = ath6kl_usb_reset_resume,
+       .suspend = ath6kl_usb_pm_suspend,
+       .resume = ath6kl_usb_pm_resume,
+       .reset_resume = ath6kl_usb_pm_reset_resume,
        .disconnect = ath6kl_usb_remove,
        .id_table = ath6kl_usb_ids,
        .supports_autosuspend = true,
index e91725b..4994bea 100644 (file)
@@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = {
        {  5,  4,  1  }, /* lvl 5 */
        {  6,  5,  1  }, /* lvl 6 */
        {  7,  6,  1  }, /* lvl 7 */
-       {  7,  6,  0  }, /* lvl 8 */
-       {  7,  7,  0  }  /* lvl 9 */
+       {  7,  7,  1  }, /* lvl 8 */
+       {  7,  8,  0  }  /* lvl 9 */
 };
 #define ATH9K_ANI_OFDM_NUM_LEVEL \
        ARRAY_SIZE(ofdm_level_table)
@@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = {
        {  4,  0  }, /* lvl 4 */
        {  5,  0  }, /* lvl 5 */
        {  6,  0  }, /* lvl 6 */
-       {  6,  0  }, /* lvl 7 (only for high rssi) */
-       {  7,  0  }  /* lvl 8 (only for high rssi) */
+       {  7,  0  }, /* lvl 7 (only for high rssi) */
+       {  8,  0  }  /* lvl 8 (only for high rssi) */
 };
 
 #define ATH9K_ANI_CCK_NUM_LEVEL \
@@ -177,10 +177,15 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
            BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
                weak_sig = true;
 
-       if (aniState->ofdmWeakSigDetect != weak_sig)
-                       ath9k_hw_ani_control(ah,
-                               ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
-                               entry_ofdm->ofdm_weak_signal_on);
+       /*
+        * OFDM Weak signal detection is always enabled for AP mode.
+        */
+       if (ah->opmode != NL80211_IFTYPE_AP &&
+           aniState->ofdmWeakSigDetect != weak_sig) {
+               ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    entry_ofdm->ofdm_weak_signal_on);
+       }
 
        if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {
                ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
@@ -363,18 +368,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
        ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning);
        ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning);
 
-       /*
-        * enable phy counters if hw supports or if not, enable phy
-        * interrupts (so we can count each one)
-        */
        ath9k_ani_restart(ah);
-
-       ENABLE_REGWRITE_BUFFER(ah);
-
-       REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
-       REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
-
-       REGWRITE_BUFFER_FLUSH(ah);
 }
 
 static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
index 78b9fa9..b54a3fb 100644 (file)
 #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi)
 
 /* units are errors per second */
-#define ATH9K_ANI_OFDM_TRIG_HIGH          3500
+#define ATH9K_ANI_OFDM_TRIG_HIGH           3500
 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
 
-/* units are errors per second */
 #define ATH9K_ANI_OFDM_TRIG_LOW           400
 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
 
-/* units are errors per second */
 #define ATH9K_ANI_CCK_TRIG_HIGH           600
-
-/* units are errors per second */
 #define ATH9K_ANI_CCK_TRIG_LOW            300
 
-#define ATH9K_ANI_NOISE_IMMUNE_LVL        4
 #define ATH9K_ANI_SPUR_IMMUNE_LVL         3
 #define ATH9K_ANI_FIRSTEP_LVL             2
 
 /* in ms */
 #define ATH9K_ANI_POLLINTERVAL            1000
 
-#define HAL_NOISE_IMMUNE_MAX              4
-#define HAL_SPUR_IMMUNE_MAX               7
-#define HAL_FIRST_STEP_MAX                2
-
 #define ATH9K_SIG_FIRSTEP_SETTING_MIN     0
 #define ATH9K_SIG_FIRSTEP_SETTING_MAX     20
 #define ATH9K_SIG_SPUR_IMM_SETTING_MIN    0
index e6b92ff..1e86977 100644 (file)
@@ -3563,14 +3563,24 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
 {
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        int chain;
-       u32 regval;
+       u32 regval, value, gpio;
        static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = {
                        AR_PHY_SWITCH_CHAIN_0,
                        AR_PHY_SWITCH_CHAIN_1,
                        AR_PHY_SWITCH_CHAIN_2,
        };
 
-       u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
+       if (AR_SREV_9485(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) {
+               if (ah->config.xlna_gpio)
+                       gpio = ah->config.xlna_gpio;
+               else
+                       gpio = AR9300_EXT_LNA_CTL_GPIO_AR9485;
+
+               ath9k_hw_cfg_output(ah, gpio,
+                                   AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED);
+       }
+
+       value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);
 
        if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
                REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
@@ -3796,7 +3806,13 @@ static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan)
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value);
 
-                       value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+                       if (AR_SREV_9485(ah) &&
+                           (ar9003_hw_get_rx_gain_idx(ah) == 0) &&
+                           ah->config.xatten_margin_cfg)
+                               value = 5;
+                       else
+                               value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+
                        REG_RMW_FIELD(ah, ext_atten_reg[i],
                                      AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
                                      value);
@@ -4546,7 +4562,7 @@ static void ar9003_hw_get_target_power_eeprom(struct ath_hw *ah,
                                                 is2GHz);
 
        for (i = 0; i < ar9300RateSize; i++) {
-               ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n",
+               ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n",
                        i, targetPowerValT2[i]);
        }
 }
@@ -5272,7 +5288,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
                return;
 
        for (i = 0; i < ar9300RateSize; i++) {
-               ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n",
+               ath_dbg(common, REGULATORY, "TPC[%02d] 0x%08x\n",
                        i, targetPowerValT2[i]);
        }
 
index a3523c9..671aaa7 100644 (file)
@@ -627,9 +627,26 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
 
 static void ar9003_rx_gain_table_mode2(struct ath_hw *ah)
 {
-       if (AR_SREV_9462_20(ah))
+       if (AR_SREV_9462_20(ah)) {
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                               ar9462_common_mixed_rx_gain_table_2p0);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                              ar9462_2p0_baseband_core_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                              ar9462_2p0_baseband_postamble_mix_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p0_baseband_postamble_5g_xlna);
+       }
+}
+
+static void ar9003_rx_gain_table_mode3(struct ath_hw *ah)
+{
+       if (AR_SREV_9462_20(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              ar9462_2p0_5g_xlna_only_rxgain);
+               INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                              ar9462_2p0_baseband_postamble_5g_xlna);
+       }
 }
 
 static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
@@ -645,6 +662,9 @@ static void ar9003_rx_gain_table_apply(struct ath_hw *ah)
        case 2:
                ar9003_rx_gain_table_mode2(ah);
                break;
+       case 3:
+               ar9003_rx_gain_table_mode3(ah);
+               break;
        }
 }
 
index 83e0385..df84d20 100644 (file)
@@ -735,6 +735,9 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                return -EINVAL;
        }
 
+       /*
+        * SOC, MAC, BB, RADIO initvals.
+        */
        for (i = 0; i < ATH_INI_NUM_SPLIT; i++) {
                ar9003_hw_prog_ini(ah, &ah->iniSOC[i], modesIndex);
                ar9003_hw_prog_ini(ah, &ah->iniMac[i], modesIndex);
@@ -746,11 +749,39 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                                           modesIndex);
        }
 
+       /*
+        * RXGAIN initvals.
+        */
        REG_WRITE_ARRAY(&ah->iniModesRxGain, 1, regWrites);
+
+       if (AR_SREV_9462_20(ah)) {
+               /*
+                * CUS217 mix LNA mode.
+                */
+               if (ar9003_hw_get_rx_gain_idx(ah) == 2) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core,
+                                       1, regWrites);
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+                                       modesIndex, regWrites);
+               }
+
+               /*
+                * 5G-XLNA
+                */
+               if ((ar9003_hw_get_rx_gain_idx(ah) == 2) ||
+                   (ar9003_hw_get_rx_gain_idx(ah) == 3)) {
+                       REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna,
+                                       modesIndex, regWrites);
+               }
+       }
+
        if (AR_SREV_9550(ah))
                REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex,
                                regWrites);
 
+       /*
+        * TXGAIN initvals.
+        */
        if (AR_SREV_9550(ah)) {
                int modes_txgain_index;
 
@@ -772,8 +803,14 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
                REG_WRITE_ARRAY(&ah->iniModesFastClock,
                                modesIndex, regWrites);
 
+       /*
+        * Clock frequency initvals.
+        */
        REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites);
 
+       /*
+        * JAPAN regulatory.
+        */
        if (chan->channel == 2484)
                ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
 
@@ -906,6 +943,11 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_channel *chan = ah->curchan;
        struct ar5416AniState *aniState = &ah->ani;
+       int m1ThreshLow, m2ThreshLow;
+       int m1Thresh, m2Thresh;
+       int m2CountThr, m2CountThrLow;
+       int m1ThreshLowExt, m2ThreshLowExt;
+       int m1ThreshExt, m2ThreshExt;
        s32 value, value2;
 
        switch (cmd & ah->ani_function) {
@@ -919,6 +961,61 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
                 */
                u32 on = param ? 1 : 0;
 
+               if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+                       goto skip_ws_det;
+
+               m1ThreshLow = on ?
+                       aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
+               m2ThreshLow = on ?
+                       aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
+               m1Thresh = on ?
+                       aniState->iniDef.m1Thresh : m1Thresh_off;
+               m2Thresh = on ?
+                       aniState->iniDef.m2Thresh : m2Thresh_off;
+               m2CountThr = on ?
+                       aniState->iniDef.m2CountThr : m2CountThr_off;
+               m2CountThrLow = on ?
+                       aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
+               m1ThreshLowExt = on ?
+                       aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
+               m2ThreshLowExt = on ?
+                       aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
+               m1ThreshExt = on ?
+                       aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
+               m2ThreshExt = on ?
+                       aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
+
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+                             m1ThreshLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+                             m2ThreshLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M1_THRESH,
+                             m1Thresh);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2_THRESH,
+                             m2Thresh);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2COUNT_THR,
+                             m2CountThr);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+                             m2CountThrLow);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+                             m1ThreshLowExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+                             m2ThreshLowExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH,
+                             m1ThreshExt);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH,
+                             m2ThreshExt);
+skip_ws_det:
                if (on)
                        REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
                                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
index e717741..5013c73 100644 (file)
 
 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ          -118
 
+#define AR9300_EXT_LNA_CTL_GPIO_AR9485 9
+
 /*
  * AGC Field Definitions
  */
index 999ab08..1d6b705 100644 (file)
@@ -78,7 +78,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
        {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
        {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
        {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
-       {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
        {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
        {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
        {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@@ -1449,4 +1449,284 @@ static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = {
        {0x0000b1fc, 0x00000196},
 };
 
+static const u32 ar9462_2p0_baseband_postamble_5g_xlna[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
+};
+
+static const u32 ar9462_2p0_5g_xlna_only_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a000, 0x00010000},
+       {0x0000a004, 0x00030002},
+       {0x0000a008, 0x00050004},
+       {0x0000a00c, 0x00810080},
+       {0x0000a010, 0x00830082},
+       {0x0000a014, 0x01810180},
+       {0x0000a018, 0x01830182},
+       {0x0000a01c, 0x01850184},
+       {0x0000a020, 0x01890188},
+       {0x0000a024, 0x018b018a},
+       {0x0000a028, 0x018d018c},
+       {0x0000a02c, 0x03820190},
+       {0x0000a030, 0x03840383},
+       {0x0000a034, 0x03880385},
+       {0x0000a038, 0x038a0389},
+       {0x0000a03c, 0x038c038b},
+       {0x0000a040, 0x0390038d},
+       {0x0000a044, 0x03920391},
+       {0x0000a048, 0x03940393},
+       {0x0000a04c, 0x03960395},
+       {0x0000a050, 0x00000000},
+       {0x0000a054, 0x00000000},
+       {0x0000a058, 0x00000000},
+       {0x0000a05c, 0x00000000},
+       {0x0000a060, 0x00000000},
+       {0x0000a064, 0x00000000},
+       {0x0000a068, 0x00000000},
+       {0x0000a06c, 0x00000000},
+       {0x0000a070, 0x00000000},
+       {0x0000a074, 0x00000000},
+       {0x0000a078, 0x00000000},
+       {0x0000a07c, 0x00000000},
+       {0x0000a080, 0x29292929},
+       {0x0000a084, 0x29292929},
+       {0x0000a088, 0x29292929},
+       {0x0000a08c, 0x29292929},
+       {0x0000a090, 0x22292929},
+       {0x0000a094, 0x1d1d2222},
+       {0x0000a098, 0x0c111117},
+       {0x0000a09c, 0x00030303},
+       {0x0000a0a0, 0x00000000},
+       {0x0000a0a4, 0x00000000},
+       {0x0000a0a8, 0x00000000},
+       {0x0000a0ac, 0x00000000},
+       {0x0000a0b0, 0x00000000},
+       {0x0000a0b4, 0x00000000},
+       {0x0000a0b8, 0x00000000},
+       {0x0000a0bc, 0x00000000},
+       {0x0000a0c0, 0x001f0000},
+       {0x0000a0c4, 0x01000101},
+       {0x0000a0c8, 0x011e011f},
+       {0x0000a0cc, 0x011c011d},
+       {0x0000a0d0, 0x02030204},
+       {0x0000a0d4, 0x02010202},
+       {0x0000a0d8, 0x021f0200},
+       {0x0000a0dc, 0x0302021e},
+       {0x0000a0e0, 0x03000301},
+       {0x0000a0e4, 0x031e031f},
+       {0x0000a0e8, 0x0402031d},
+       {0x0000a0ec, 0x04000401},
+       {0x0000a0f0, 0x041e041f},
+       {0x0000a0f4, 0x0502041d},
+       {0x0000a0f8, 0x05000501},
+       {0x0000a0fc, 0x051e051f},
+       {0x0000a100, 0x06010602},
+       {0x0000a104, 0x061f0600},
+       {0x0000a108, 0x061d061e},
+       {0x0000a10c, 0x07020703},
+       {0x0000a110, 0x07000701},
+       {0x0000a114, 0x00000000},
+       {0x0000a118, 0x00000000},
+       {0x0000a11c, 0x00000000},
+       {0x0000a120, 0x00000000},
+       {0x0000a124, 0x00000000},
+       {0x0000a128, 0x00000000},
+       {0x0000a12c, 0x00000000},
+       {0x0000a130, 0x00000000},
+       {0x0000a134, 0x00000000},
+       {0x0000a138, 0x00000000},
+       {0x0000a13c, 0x00000000},
+       {0x0000a140, 0x001f0000},
+       {0x0000a144, 0x01000101},
+       {0x0000a148, 0x011e011f},
+       {0x0000a14c, 0x011c011d},
+       {0x0000a150, 0x02030204},
+       {0x0000a154, 0x02010202},
+       {0x0000a158, 0x021f0200},
+       {0x0000a15c, 0x0302021e},
+       {0x0000a160, 0x03000301},
+       {0x0000a164, 0x031e031f},
+       {0x0000a168, 0x0402031d},
+       {0x0000a16c, 0x04000401},
+       {0x0000a170, 0x041e041f},
+       {0x0000a174, 0x0502041d},
+       {0x0000a178, 0x05000501},
+       {0x0000a17c, 0x051e051f},
+       {0x0000a180, 0x06010602},
+       {0x0000a184, 0x061f0600},
+       {0x0000a188, 0x061d061e},
+       {0x0000a18c, 0x07020703},
+       {0x0000a190, 0x07000701},
+       {0x0000a194, 0x00000000},
+       {0x0000a198, 0x00000000},
+       {0x0000a19c, 0x00000000},
+       {0x0000a1a0, 0x00000000},
+       {0x0000a1a4, 0x00000000},
+       {0x0000a1a8, 0x00000000},
+       {0x0000a1ac, 0x00000000},
+       {0x0000a1b0, 0x00000000},
+       {0x0000a1b4, 0x00000000},
+       {0x0000a1b8, 0x00000000},
+       {0x0000a1bc, 0x00000000},
+       {0x0000a1c0, 0x00000000},
+       {0x0000a1c4, 0x00000000},
+       {0x0000a1c8, 0x00000000},
+       {0x0000a1cc, 0x00000000},
+       {0x0000a1d0, 0x00000000},
+       {0x0000a1d4, 0x00000000},
+       {0x0000a1d8, 0x00000000},
+       {0x0000a1dc, 0x00000000},
+       {0x0000a1e0, 0x00000000},
+       {0x0000a1e4, 0x00000000},
+       {0x0000a1e8, 0x00000000},
+       {0x0000a1ec, 0x00000000},
+       {0x0000a1f0, 0x00000396},
+       {0x0000a1f4, 0x00000396},
+       {0x0000a1f8, 0x00000396},
+       {0x0000a1fc, 0x00000196},
+       {0x0000b000, 0x00010000},
+       {0x0000b004, 0x00030002},
+       {0x0000b008, 0x00050004},
+       {0x0000b00c, 0x00810080},
+       {0x0000b010, 0x00830082},
+       {0x0000b014, 0x01810180},
+       {0x0000b018, 0x01830182},
+       {0x0000b01c, 0x01850184},
+       {0x0000b020, 0x02810280},
+       {0x0000b024, 0x02830282},
+       {0x0000b028, 0x02850284},
+       {0x0000b02c, 0x02890288},
+       {0x0000b030, 0x028b028a},
+       {0x0000b034, 0x0388028c},
+       {0x0000b038, 0x038a0389},
+       {0x0000b03c, 0x038c038b},
+       {0x0000b040, 0x0390038d},
+       {0x0000b044, 0x03920391},
+       {0x0000b048, 0x03940393},
+       {0x0000b04c, 0x03960395},
+       {0x0000b050, 0x00000000},
+       {0x0000b054, 0x00000000},
+       {0x0000b058, 0x00000000},
+       {0x0000b05c, 0x00000000},
+       {0x0000b060, 0x00000000},
+       {0x0000b064, 0x00000000},
+       {0x0000b068, 0x00000000},
+       {0x0000b06c, 0x00000000},
+       {0x0000b070, 0x00000000},
+       {0x0000b074, 0x00000000},
+       {0x0000b078, 0x00000000},
+       {0x0000b07c, 0x00000000},
+       {0x0000b080, 0x2a2d2f32},
+       {0x0000b084, 0x21232328},
+       {0x0000b088, 0x19191c1e},
+       {0x0000b08c, 0x12141417},
+       {0x0000b090, 0x07070e0e},
+       {0x0000b094, 0x03030305},
+       {0x0000b098, 0x00000003},
+       {0x0000b09c, 0x00000000},
+       {0x0000b0a0, 0x00000000},
+       {0x0000b0a4, 0x00000000},
+       {0x0000b0a8, 0x00000000},
+       {0x0000b0ac, 0x00000000},
+       {0x0000b0b0, 0x00000000},
+       {0x0000b0b4, 0x00000000},
+       {0x0000b0b8, 0x00000000},
+       {0x0000b0bc, 0x00000000},
+       {0x0000b0c0, 0x003f0020},
+       {0x0000b0c4, 0x00400041},
+       {0x0000b0c8, 0x0140005f},
+       {0x0000b0cc, 0x0160015f},
+       {0x0000b0d0, 0x017e017f},
+       {0x0000b0d4, 0x02410242},
+       {0x0000b0d8, 0x025f0240},
+       {0x0000b0dc, 0x027f0260},
+       {0x0000b0e0, 0x0341027e},
+       {0x0000b0e4, 0x035f0340},
+       {0x0000b0e8, 0x037f0360},
+       {0x0000b0ec, 0x04400441},
+       {0x0000b0f0, 0x0460045f},
+       {0x0000b0f4, 0x0541047f},
+       {0x0000b0f8, 0x055f0540},
+       {0x0000b0fc, 0x057f0560},
+       {0x0000b100, 0x06400641},
+       {0x0000b104, 0x0660065f},
+       {0x0000b108, 0x067e067f},
+       {0x0000b10c, 0x07410742},
+       {0x0000b110, 0x075f0740},
+       {0x0000b114, 0x077f0760},
+       {0x0000b118, 0x07800781},
+       {0x0000b11c, 0x07a0079f},
+       {0x0000b120, 0x07c107bf},
+       {0x0000b124, 0x000007c0},
+       {0x0000b128, 0x00000000},
+       {0x0000b12c, 0x00000000},
+       {0x0000b130, 0x00000000},
+       {0x0000b134, 0x00000000},
+       {0x0000b138, 0x00000000},
+       {0x0000b13c, 0x00000000},
+       {0x0000b140, 0x003f0020},
+       {0x0000b144, 0x00400041},
+       {0x0000b148, 0x0140005f},
+       {0x0000b14c, 0x0160015f},
+       {0x0000b150, 0x017e017f},
+       {0x0000b154, 0x02410242},
+       {0x0000b158, 0x025f0240},
+       {0x0000b15c, 0x027f0260},
+       {0x0000b160, 0x0341027e},
+       {0x0000b164, 0x035f0340},
+       {0x0000b168, 0x037f0360},
+       {0x0000b16c, 0x04400441},
+       {0x0000b170, 0x0460045f},
+       {0x0000b174, 0x0541047f},
+       {0x0000b178, 0x055f0540},
+       {0x0000b17c, 0x057f0560},
+       {0x0000b180, 0x06400641},
+       {0x0000b184, 0x0660065f},
+       {0x0000b188, 0x067e067f},
+       {0x0000b18c, 0x07410742},
+       {0x0000b190, 0x075f0740},
+       {0x0000b194, 0x077f0760},
+       {0x0000b198, 0x07800781},
+       {0x0000b19c, 0x07a0079f},
+       {0x0000b1a0, 0x07c107bf},
+       {0x0000b1a4, 0x000007c0},
+       {0x0000b1a8, 0x00000000},
+       {0x0000b1ac, 0x00000000},
+       {0x0000b1b0, 0x00000000},
+       {0x0000b1b4, 0x00000000},
+       {0x0000b1b8, 0x00000000},
+       {0x0000b1bc, 0x00000000},
+       {0x0000b1c0, 0x00000000},
+       {0x0000b1c4, 0x00000000},
+       {0x0000b1c8, 0x00000000},
+       {0x0000b1cc, 0x00000000},
+       {0x0000b1d0, 0x00000000},
+       {0x0000b1d4, 0x00000000},
+       {0x0000b1d8, 0x00000000},
+       {0x0000b1dc, 0x00000000},
+       {0x0000b1e0, 0x00000000},
+       {0x0000b1e4, 0x00000000},
+       {0x0000b1e8, 0x00000000},
+       {0x0000b1ec, 0x00000000},
+       {0x0000b1f0, 0x00000396},
+       {0x0000b1f4, 0x00000396},
+       {0x0000b1f8, 0x00000396},
+       {0x0000b1fc, 0x00000196},
+};
+
+static const u32 ar9462_2p0_baseband_core_mix_rxgain[][2] = {
+       /* Addr      allmodes  */
+       {0x00009fd0, 0x0a2d6b93},
+};
+
+static const u32 ar9462_2p0_baseband_postamble_mix_rxgain[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae},
+       {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da},
+       {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8},
+       {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e},
+};
+
 #endif /* INITVALS_9462_2P0_H */
index 18fcee4..04b2d3e 100644 (file)
@@ -296,6 +296,7 @@ struct ath_tx {
        struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
        struct ath_descdma txdma;
        struct ath_txq *txq_map[IEEE80211_NUM_ACS];
+       struct ath_txq *uapsdq;
        u32 txq_max_pending[IEEE80211_NUM_ACS];
        u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
 };
@@ -343,6 +344,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
 void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop);
 int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 struct ath_tx_control *txctl);
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb);
 void ath_tx_tasklet(struct ath_softc *sc);
 void ath_tx_edma_tasklet(struct ath_softc *sc);
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -353,6 +356,11 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
                       struct ath_node *an);
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+                                  struct ieee80211_sta *sta,
+                                  u16 tids, int nframes,
+                                  enum ieee80211_frame_release_type reason,
+                                  bool more_data);
 
 /********/
 /* VIFs */
@@ -623,6 +631,10 @@ void ath_ant_comb_update(struct ath_softc *sc);
 /* Main driver core */
 /********************/
 
+#define ATH9K_PCI_CUS198 0x0001
+#define ATH9K_PCI_CUS230 0x0002
+#define ATH9K_PCI_CUS217 0x0004
+
 /*
  * Default cache line size, in bytes.
  * Used when PCI device not fully initialized by bootrom/BIOS
@@ -707,6 +719,7 @@ struct ath_softc {
 
        unsigned int hw_busy_count;
        unsigned long sc_flags;
+       unsigned long driver_data;
 
        u32 intrstatus;
        u16 ps_flags; /* PS_* */
index fd1eeba..1a17732 100644 (file)
@@ -108,23 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 }
 
-static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
-       struct ath_softc *sc = hw->priv;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_tx_control txctl;
-
-       memset(&txctl, 0, sizeof(struct ath_tx_control));
-       txctl.txq = sc->beacon.cabq;
-
-       ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb);
-
-       if (ath_tx_start(hw, skb, &txctl) != 0) {
-               ath_dbg(common, XMIT, "CABQ TX failed\n");
-               ieee80211_free_txskb(hw, skb);
-       }
-}
-
 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif)
 {
@@ -206,10 +189,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
 
        ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx);
 
-       while (skb) {
-               ath9k_tx_cabq(hw, skb);
-               skb = ieee80211_get_buffered_bc(hw, vif);
-       }
+       if (skb)
+               ath_tx_cabq(hw, vif, skb);
 
        return bf;
 }
index 7304e75..5e8219a 100644 (file)
@@ -387,7 +387,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan)
 
        if (!caldata) {
                chan->noisefloor = nf;
-               ah->noise = ath9k_hw_getchan_noise(ah, chan);
                return false;
        }
 
index 0085e64..6958103 100644 (file)
@@ -142,6 +142,7 @@ struct ath9k_htc_target_aggr {
 #define WLAN_RC_40_FLAG  0x02
 #define WLAN_RC_SGI_FLAG 0x04
 #define WLAN_RC_HT_FLAG  0x08
+#define ATH_RC_TX_STBC_FLAG 0x20
 
 struct ath9k_htc_rateset {
        u8 rs_nrates;
index 59f6436..bb0ba9e 100644 (file)
@@ -517,6 +517,9 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv,
        ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n",
                tx_streams, rx_streams);
 
+       if (tx_streams >= 2)
+               ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+
        if (tx_streams != rx_streams) {
                ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
                ht_info->mcs.tx_params |= ((tx_streams - 1) <<
index 34869c2..eaa94fe 100644 (file)
@@ -627,6 +627,8 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
                trate->rates.ht_rates.rs_nrates = j;
 
                caps = WLAN_RC_HT_FLAG;
+               if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+                       caps |= ATH_RC_TX_STBC_FLAG;
                if (sta->ht_cap.mcs.rx_mask[1])
                        caps |= WLAN_RC_DS_FLAG;
                if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
index d813ab8..5324c33 100644 (file)
@@ -1870,7 +1870,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        ah->caldata = caldata;
        if (caldata && (chan->channel != caldata->channel ||
-                       chan->channelFlags != caldata->channelFlags)) {
+                       chan->channelFlags != caldata->channelFlags ||
+                       chan->chanmode != caldata->chanmode)) {
                /* Operating channel changed, reset channel calibration data */
                memset(caldata, 0, sizeof(*caldata));
                ath9k_init_nfcal_hist_buffer(ah, chan);
@@ -3041,7 +3042,7 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
 
        timer_next = tsf + trig_timeout;
 
-       ath_dbg(ath9k_hw_common(ah), HWTIMER,
+       ath_dbg(ath9k_hw_common(ah), BTCOEX,
                "current tsf %x period %x timer_next %x\n",
                tsf, timer_period, timer_next);
 
@@ -3140,7 +3141,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
                index = rightmost_index(timer_table, &thresh_mask);
                timer = timer_table->timers[index];
                BUG_ON(!timer);
-               ath_dbg(common, HWTIMER, "TSF overflow for Gen timer %d\n",
+               ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n",
                        index);
                timer->overflow(timer->arg);
        }
@@ -3149,7 +3150,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
                index = rightmost_index(timer_table, &trigger_mask);
                timer = timer_table->timers[index];
                BUG_ON(!timer);
-               ath_dbg(common, HWTIMER,
+               ath_dbg(common, BTCOEX,
                        "Gen timer[%d] trigger\n", index);
                timer->trigger(timer->arg);
        }
index 7d259b7..cd74b3a 100644 (file)
@@ -307,6 +307,10 @@ struct ath9k_ops_config {
        u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
        u8 max_txtrig_level;
        u16 ani_poll_interval; /* ANI poll interval in ms */
+
+       /* Platform specific config */
+       u32 xlna_gpio;
+       bool xatten_margin_cfg;
 };
 
 enum ath9k_int {
@@ -888,6 +892,9 @@ struct ath_hw {
        struct ar5416IniArray iniCckfirJapan2484;
        struct ar5416IniArray iniModes_9271_ANI_reg;
        struct ar5416IniArray ini_radio_post_sys2ant;
+       struct ar5416IniArray ini_modes_rxgain_5g_xlna;
+       struct ar5416IniArray ini_modes_rxgain_bb_core;
+       struct ar5416IniArray ini_modes_rxgain_bb_postamble;
 
        struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT];
        struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT];
index 389ee1b..1e555d8 100644 (file)
@@ -432,6 +432,8 @@ static int ath9k_init_queues(struct ath_softc *sc)
        sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
        ath_cabq_update(sc);
 
+       sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
                sc->tx.txq_map[i]->mac80211_qnum = i;
@@ -511,6 +513,27 @@ static void ath9k_init_misc(struct ath_softc *sc)
        sc->spec_config.fft_period = 0xF;
 }
 
+static void ath9k_init_platform(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (common->bus_ops->ath_bus_type != ATH_PCI)
+               return;
+
+       if (sc->driver_data & (ATH9K_PCI_CUS198 |
+                              ATH9K_PCI_CUS230)) {
+               ah->config.xlna_gpio = 9;
+               ah->config.xatten_margin_cfg = true;
+
+               ath_info(common, "Set parameters for %s\n",
+                        (sc->driver_data & ATH9K_PCI_CUS198) ?
+                        "CUS198" : "CUS230");
+       } else if (sc->driver_data & ATH9K_PCI_CUS217) {
+               ath_info(common, "CUS217 card detected\n");
+       }
+}
+
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
                                    void *ctx)
 {
@@ -602,6 +625,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        common->btcoex_enabled = ath9k_btcoex_enable == 1;
        common->disable_ani = false;
 
+       /*
+        * Platform quirks.
+        */
+       ath9k_init_platform(sc);
+
        /*
         * Enable Antenna diversity only when BTCOEX is disabled
         * and the user manually requests the feature.
@@ -753,6 +781,15 @@ static const struct ieee80211_iface_combination if_comb[] = {
        }
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support ath9k_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+       .n_patterns = MAX_NUM_USER_PATTERN,
+       .pattern_min_len = 1,
+       .pattern_max_len = MAX_PATTERN_SIZE,
+};
+#endif
+
 void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -800,13 +837,8 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 
 #ifdef CONFIG_PM_SLEEP
        if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
-           device_can_wakeup(sc->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT;
-               hw->wiphy->wowlan.n_patterns = MAX_NUM_USER_PATTERN;
-               hw->wiphy->wowlan.pattern_min_len = 1;
-               hw->wiphy->wowlan.pattern_max_len = MAX_PATTERN_SIZE;
-       }
+           device_can_wakeup(sc->dev))
+               hw->wiphy->wowlan = &ath9k_wowlan_support;
 
        atomic_set(&sc->wow_sleep_proc_intr, -1);
        atomic_set(&sc->wow_got_bmiss_intr, -1);
index e5b186b..1737a3e 100644 (file)
@@ -1210,13 +1210,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                ath_update_survey_stats(sc);
                spin_unlock_irqrestore(&common->cc_lock, flags);
 
-               /*
-                * Preserve the current channel values, before updating
-                * the same channel
-                */
-               if (ah->curchan && (old_pos == pos))
-                       ath9k_hw_getnf(ah, ah->curchan);
-
                ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
                                          curchan, channel_type);
 
@@ -2347,6 +2340,7 @@ struct ieee80211_ops ath9k_ops = {
        .flush              = ath9k_flush,
        .tx_frames_pending  = ath9k_tx_frames_pending,
        .tx_last_beacon     = ath9k_tx_last_beacon,
+       .release_buffered_frames = ath9k_release_buffered_frames,
        .get_stats          = ath9k_get_stats,
        .set_antenna        = ath9k_set_antenna,
        .get_antenna        = ath9k_get_antenna,
index 0e0d395..b096bb2 100644 (file)
@@ -34,8 +34,51 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI   */
        { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */
        { PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E  AR9300 */
+
+       /* PCI-E CUS198 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2086),
+         .driver_data = ATH9K_PCI_CUS198 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x1237),
+         .driver_data = ATH9K_PCI_CUS198 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2126),
+         .driver_data = ATH9K_PCI_CUS198 },
+
+       /* PCI-E CUS230 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2152),
+         .driver_data = ATH9K_PCI_CUS230 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0032,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE075),
+         .driver_data = ATH9K_PCI_CUS230 },
+
        { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E  AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E  AR9580 */
+
+       /* PCI-E CUS217 */
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2116),
+         .driver_data = ATH9K_PCI_CUS217 },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0034,
+                        0x11AD, /* LITEON */
+                        0x6661),
+         .driver_data = ATH9K_PCI_CUS217 },
+
        { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E  AR9462 */
        { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E  AR1111/AR9485 */
        { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E  AR9565 */
@@ -221,6 +264,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sc->hw = hw;
        sc->dev = &pdev->dev;
        sc->mem = pcim_iomap_table(pdev)[0];
+       sc->driver_data = id->driver_data;
 
        /* Will be cleared in ath9k_start() */
        set_bit(SC_OP_INVALID, &sc->sc_flags);
index 1c9b1ba..7e19d9b 100644 (file)
@@ -518,6 +518,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
                                !txfail);
                } else {
+                       if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) {
+                               tx_info->flags &= ~IEEE80211_TX_STATUS_EOSP;
+                               ieee80211_sta_eosp(sta);
+                       }
                        /* retry the un-acked ones */
                        if (bf->bf_next == NULL && bf_last->bf_stale) {
                                struct ath_buf *tbf;
@@ -786,25 +790,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
        return ndelim;
 }
 
-static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
-                                            struct ath_txq *txq,
-                                            struct ath_atx_tid *tid,
-                                            struct list_head *bf_q,
-                                            int *aggr_len)
+static struct ath_buf *
+ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
+                       struct ath_atx_tid *tid)
 {
-#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
-       struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
-       int rl = 0, nframes = 0, ndelim, prev_al = 0;
-       u16 aggr_limit = 0, al = 0, bpad = 0,
-               al_delta, h_baw = tid->baw_size / 2;
-       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
-       struct ieee80211_tx_info *tx_info;
        struct ath_frame_info *fi;
        struct sk_buff *skb;
+       struct ath_buf *bf;
        u16 seqno;
 
-       do {
+       while (1) {
                skb = skb_peek(&tid->buf_q);
+               if (!skb)
+                       break;
+
                fi = get_frame_info(skb);
                bf = fi->bf;
                if (!fi->bf)
@@ -820,10 +819,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                seqno = bf->bf_state.seqno;
 
                /* do not step over block-ack window */
-               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
-                       status = ATH_AGGR_BAW_CLOSED;
+               if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
                        break;
-               }
 
                if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
                        struct ath_tx_status ts = {};
@@ -837,6 +834,40 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                        continue;
                }
 
+               bf->bf_next = NULL;
+               bf->bf_lastbf = bf;
+               return bf;
+       }
+
+       return NULL;
+}
+
+static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
+                                            struct ath_txq *txq,
+                                            struct ath_atx_tid *tid,
+                                            struct list_head *bf_q,
+                                            int *aggr_len)
+{
+#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
+       struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
+       int rl = 0, nframes = 0, ndelim, prev_al = 0;
+       u16 aggr_limit = 0, al = 0, bpad = 0,
+               al_delta, h_baw = tid->baw_size / 2;
+       enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
+       struct ieee80211_tx_info *tx_info;
+       struct ath_frame_info *fi;
+       struct sk_buff *skb;
+
+       do {
+               bf = ath_tx_get_tid_subframe(sc, txq, tid);
+               if (!bf) {
+                       status = ATH_AGGR_BAW_CLOSED;
+                       break;
+               }
+
+               skb = bf->bf_mpdu;
+               fi = get_frame_info(skb);
+
                if (!bf_first)
                        bf_first = bf;
 
@@ -882,7 +913,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
 
                /* link buffers of this frame to the aggregate */
                if (!fi->retries)
-                       ath_tx_addto_baw(sc, tid, seqno);
+                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
                bf->bf_state.ndelim = ndelim;
 
                __skb_unlink(skb, &tid->buf_q);
@@ -1090,10 +1121,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                             struct ath_txq *txq, int len)
 {
        struct ath_hw *ah = sc->sc_ah;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-       struct ath_buf *bf_first = bf;
+       struct ath_buf *bf_first = NULL;
        struct ath_tx_info info;
-       bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
        memset(&info, 0, sizeof(info));
        info.is_first = true;
@@ -1101,24 +1130,11 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
        info.txpower = MAX_RATE_POWER;
        info.qcu = txq->axq_qnum;
 
-       info.flags = ATH9K_TXDESC_INTREQ;
-       if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
-               info.flags |= ATH9K_TXDESC_NOACK;
-       if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
-               info.flags |= ATH9K_TXDESC_LDPC;
-
-       ath_buf_set_rate(sc, bf, &info, len);
-
-       if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
-               info.flags |= ATH9K_TXDESC_CLRDMASK;
-
-       if (bf->bf_state.bfs_paprd)
-               info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S;
-
-
        while (bf) {
                struct sk_buff *skb = bf->bf_mpdu;
+               struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
                struct ath_frame_info *fi = get_frame_info(skb);
+               bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR);
 
                info.type = get_hw_packet_type(skb);
                if (bf->bf_next)
@@ -1126,6 +1142,26 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                else
                        info.link = 0;
 
+               if (!bf_first) {
+                       bf_first = bf;
+
+                       info.flags = ATH9K_TXDESC_INTREQ;
+                       if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) ||
+                           txq == sc->tx.uapsdq)
+                               info.flags |= ATH9K_TXDESC_CLRDMASK;
+
+                       if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+                               info.flags |= ATH9K_TXDESC_NOACK;
+                       if (tx_info->flags & IEEE80211_TX_CTL_LDPC)
+                               info.flags |= ATH9K_TXDESC_LDPC;
+
+                       if (bf->bf_state.bfs_paprd)
+                               info.flags |= (u32) bf->bf_state.bfs_paprd <<
+                                             ATH9K_TXDESC_PAPRD_S;
+
+                       ath_buf_set_rate(sc, bf, &info, len);
+               }
+
                info.buf_addr[0] = bf->bf_buf_addr;
                info.buf_len[0] = skb->len;
                info.pkt_len = fi->framelen;
@@ -1135,7 +1171,7 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                if (aggr) {
                        if (bf == bf_first)
                                info.aggr = AGGR_BUF_FIRST;
-                       else if (!bf->bf_next)
+                       else if (bf == bf_first->bf_lastbf)
                                info.aggr = AGGR_BUF_LAST;
                        else
                                info.aggr = AGGR_BUF_MIDDLE;
@@ -1144,6 +1180,9 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                        info.aggr_len = len;
                }
 
+               if (bf == bf_first->bf_lastbf)
+                       bf_first = NULL;
+
                ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
                bf = bf->bf_next;
        }
@@ -1328,6 +1367,70 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
        ath_txq_unlock_complete(sc, txq);
 }
 
+void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
+                                  struct ieee80211_sta *sta,
+                                  u16 tids, int nframes,
+                                  enum ieee80211_frame_release_type reason,
+                                  bool more_data)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_node *an = (struct ath_node *)sta->drv_priv;
+       struct ath_txq *txq = sc->tx.uapsdq;
+       struct ieee80211_tx_info *info;
+       struct list_head bf_q;
+       struct ath_buf *bf_tail = NULL, *bf;
+       int sent = 0;
+       int i;
+
+       INIT_LIST_HEAD(&bf_q);
+       for (i = 0; tids && nframes; i++, tids >>= 1) {
+               struct ath_atx_tid *tid;
+
+               if (!(tids & 1))
+                       continue;
+
+               tid = ATH_AN_2_TID(an, i);
+               if (tid->paused)
+                       continue;
+
+               ath_txq_lock(sc, tid->ac->txq);
+               while (!skb_queue_empty(&tid->buf_q) && nframes > 0) {
+                       bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
+                       if (!bf)
+                               break;
+
+                       __skb_unlink(bf->bf_mpdu, &tid->buf_q);
+                       list_add_tail(&bf->list, &bf_q);
+                       ath_set_rates(tid->an->vif, tid->an->sta, bf);
+                       ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+                       bf->bf_state.bf_type &= ~BUF_AGGR;
+                       if (bf_tail)
+                               bf_tail->bf_next = bf;
+
+                       bf_tail = bf;
+                       nframes--;
+                       sent++;
+                       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
+
+                       if (skb_queue_empty(&tid->buf_q))
+                               ieee80211_sta_set_buffered(an->sta, i, false);
+               }
+               ath_txq_unlock_complete(sc, tid->ac->txq);
+       }
+
+       if (list_empty(&bf_q))
+               return;
+
+       info = IEEE80211_SKB_CB(bf_tail->bf_mpdu);
+       info->flags |= IEEE80211_TX_STATUS_EOSP;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       ath_txq_lock(sc, txq);
+       ath_tx_fill_desc(sc, bf, txq, 0);
+       ath_tx_txqaddbuf(sc, txq, &bf_q, false);
+       ath_txq_unlock(sc, txq);
+}
+
 /********************/
 /* Queue Management */
 /********************/
@@ -1681,8 +1784,9 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
        }
 }
 
-static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
-                             struct sk_buff *skb, struct ath_tx_control *txctl)
+static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq,
+                             struct ath_atx_tid *tid, struct sk_buff *skb,
+                             struct ath_tx_control *txctl)
 {
        struct ath_frame_info *fi = get_frame_info(skb);
        struct list_head bf_head;
@@ -1695,21 +1799,22 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
         * - seqno is not within block-ack window
         * - h/w queue depth exceeds low water mark
         */
-       if (!skb_queue_empty(&tid->buf_q) || tid->paused ||
-           !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
-           txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) {
+       if ((!skb_queue_empty(&tid->buf_q) || tid->paused ||
+            !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
+            txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) &&
+           txq != sc->tx.uapsdq) {
                /*
                 * Add this frame to software queue for scheduling later
                 * for aggregation.
                 */
-               TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw);
+               TX_STAT_INC(txq->axq_qnum, a_queued_sw);
                __skb_queue_tail(&tid->buf_q, skb);
                if (!txctl->an || !txctl->an->sleeping)
-                       ath_tx_queue_tid(txctl->txq, tid);
+                       ath_tx_queue_tid(txq, tid);
                return;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
        if (!bf) {
                ieee80211_free_txskb(sc->hw, skb);
                return;
@@ -1724,10 +1829,10 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
        ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
 
        /* Queue to h/w without aggregation */
-       TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw);
+       TX_STAT_INC(txq->axq_qnum, a_queued_hw);
        bf->bf_lastbf = bf;
-       ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen);
-       ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false);
+       ath_tx_fill_desc(sc, bf, txq, fi->framelen);
+       ath_tx_txqaddbuf(sc, txq, &bf_head, false);
 }
 
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1865,22 +1970,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
        return bf;
 }
 
-/* Upon failure caller should free skb */
-int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
-                struct ath_tx_control *txctl)
+static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+                         struct ath_tx_control *txctl)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
        struct ath_softc *sc = hw->priv;
-       struct ath_txq *txq = txctl->txq;
-       struct ath_atx_tid *tid = NULL;
-       struct ath_buf *bf;
-       int padpos, padsize;
        int frmlen = skb->len + FCS_LEN;
-       u8 tidno;
-       int q;
+       int padpos, padsize;
 
        /* NOTE:  sta can be NULL according to net/mac80211.h */
        if (sta)
@@ -1901,6 +2000,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
        }
 
+       if ((vif && vif->type != NL80211_IFTYPE_AP &&
+                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
+           !ieee80211_is_data(hdr->frame_control))
+               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+
        /* Add the padding after the header if this is not already done */
        padpos = ieee80211_hdrlen(hdr->frame_control);
        padsize = padpos & 3;
@@ -1910,16 +2014,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
 
                skb_push(skb, padsize);
                memmove(skb->data, skb->data + padsize, padpos);
-               hdr = (struct ieee80211_hdr *) skb->data;
        }
 
-       if ((vif && vif->type != NL80211_IFTYPE_AP &&
-                   vif->type != NL80211_IFTYPE_AP_VLAN) ||
-           !ieee80211_is_data(hdr->frame_control))
-               info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
-
        setup_frame_info(hw, sta, skb, frmlen);
+       return 0;
+}
 
+
+/* Upon failure caller should free skb */
+int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
+                struct ath_tx_control *txctl)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sta *sta = txctl->sta;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct ath_softc *sc = hw->priv;
+       struct ath_txq *txq = txctl->txq;
+       struct ath_atx_tid *tid = NULL;
+       struct ath_buf *bf;
+       u8 tidno;
+       int q;
+       int ret;
+
+       ret = ath_tx_prepare(hw, skb, txctl);
+       if (ret)
+           return ret;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
        /*
         * At this point, the vif, hw_key and sta pointers in the tx control
         * info are no longer valid (overwritten by the ath_frame_info data.
@@ -1935,6 +2057,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                txq->stopped = true;
        }
 
+       if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+               ath_txq_unlock(sc, txq);
+               txq = sc->tx.uapsdq;
+               ath_txq_lock(sc, txq);
+       }
+
        if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) {
                tidno = ieee80211_get_qos_ctl(hdr)[0] &
                        IEEE80211_QOS_CTL_TID_MASK;
@@ -1948,11 +2076,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                 * Try aggregation if it's a unicast data frame
                 * and the destination is HT capable.
                 */
-               ath_tx_send_ampdu(sc, tid, skb, txctl);
+               ath_tx_send_ampdu(sc, txq, tid, skb, txctl);
                goto out;
        }
 
-       bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb);
+       bf = ath_tx_setup_buffer(sc, txq, tid, skb);
        if (!bf) {
                if (txctl->paprd)
                        dev_kfree_skb_any(skb);
@@ -1967,7 +2095,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                bf->bf_state.bfs_paprd_timestamp = jiffies;
 
        ath_set_rates(vif, sta, bf);
-       ath_tx_send_normal(sc, txctl->txq, tid, skb);
+       ath_tx_send_normal(sc, txq, tid, skb);
 
 out:
        ath_txq_unlock(sc, txq);
@@ -1975,6 +2103,74 @@ out:
        return 0;
 }
 
+void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                struct sk_buff *skb)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_tx_control txctl = {
+               .txq = sc->beacon.cabq
+       };
+       struct ath_tx_info info = {};
+       struct ieee80211_hdr *hdr;
+       struct ath_buf *bf_tail = NULL;
+       struct ath_buf *bf;
+       LIST_HEAD(bf_q);
+       int duration = 0;
+       int max_duration;
+
+       max_duration =
+               sc->cur_beacon_conf.beacon_interval * 1000 *
+               sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+
+       do {
+               struct ath_frame_info *fi = get_frame_info(skb);
+
+               if (ath_tx_prepare(hw, skb, &txctl))
+                       break;
+
+               bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb);
+               if (!bf)
+                       break;
+
+               bf->bf_lastbf = bf;
+               ath_set_rates(vif, NULL, bf);
+               ath_buf_set_rate(sc, bf, &info, fi->framelen);
+               duration += info.rates[0].PktDuration;
+               if (bf_tail)
+                       bf_tail->bf_next = bf;
+
+               list_add_tail(&bf->list, &bf_q);
+               bf_tail = bf;
+               skb = NULL;
+
+               if (duration > max_duration)
+                       break;
+
+               skb = ieee80211_get_buffered_bc(hw, vif);
+       } while(skb);
+
+       if (skb)
+               ieee80211_free_txskb(hw, skb);
+
+       if (list_empty(&bf_q))
+               return;
+
+       bf = list_first_entry(&bf_q, struct ath_buf, list);
+       hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
+
+       if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) {
+               hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA;
+               dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+                       sizeof(*hdr), DMA_TO_DEVICE);
+       }
+
+       ath_txq_lock(sc, txctl.txq);
+       ath_tx_fill_desc(sc, bf, txctl.txq, 0);
+       ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false);
+       TX_STAT_INC(txctl.txq->axq_qnum, queued);
+       ath_txq_unlock(sc, txctl.txq);
+}
+
 /*****************/
 /* TX Completion */
 /*****************/
@@ -2020,7 +2216,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
        }
        spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
+       __skb_queue_tail(&txq->complete_q, skb);
+
        q = skb_get_queue_mapping(skb);
+       if (txq == sc->tx.uapsdq)
+               txq = sc->tx.txq_map[q];
+
        if (txq == sc->tx.txq_map[q]) {
                if (WARN_ON(--txq->pending_frames < 0))
                        txq->pending_frames = 0;
@@ -2031,8 +2232,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
                        txq->stopped = false;
                }
        }
-
-       __skb_queue_tail(&txq->complete_q, skb);
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
index 9dce106..8596aba 100644 (file)
@@ -133,6 +133,9 @@ struct carl9170_sta_tid {
 
        /* Preaggregation reorder queue */
        struct sk_buff_head queue;
+
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif;
 };
 
 #define CARL9170_QUEUE_TIMEOUT         256
index e9010a4..4a33c6e 100644 (file)
@@ -1448,6 +1448,8 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                tid_info->state = CARL9170_TID_STATE_PROGRESS;
                tid_info->tid = tid;
                tid_info->max = sta_info->ampdu_max_len;
+               tid_info->sta = sta;
+               tid_info->vif = vif;
 
                INIT_LIST_HEAD(&tid_info->list);
                INIT_LIST_HEAD(&tid_info->tmp_list);
@@ -1857,6 +1859,7 @@ void *carl9170_alloc(size_t priv_size)
                     IEEE80211_HW_SUPPORTS_PS |
                     IEEE80211_HW_PS_NULLFUNC_STACK |
                     IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
+                    IEEE80211_HW_SUPPORTS_RC_TABLE |
                     IEEE80211_HW_SIGNAL_DBM;
 
        if (!modparam_noht) {
index c61cafa..e3f696e 100644 (file)
@@ -625,7 +625,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar)
                    msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)))
                        goto unlock;
 
-               sta = __carl9170_get_tx_sta(ar, skb);
+               sta = iter->sta;
                if (WARN_ON(!sta))
                        goto unlock;
 
@@ -866,6 +866,93 @@ static bool carl9170_tx_cts_check(struct ar9170 *ar,
        return false;
 }
 
+static void carl9170_tx_get_rates(struct ar9170 *ar,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta,
+                                 struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info;
+
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
+       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES > IEEE80211_TX_RATE_TABLE_SIZE);
+
+       info = IEEE80211_SKB_CB(skb);
+
+       ieee80211_get_tx_rates(vif, sta, skb,
+                              info->control.rates,
+                              IEEE80211_TX_MAX_RATES);
+}
+
+static void carl9170_tx_apply_rateset(struct ar9170 *ar,
+                                     struct ieee80211_tx_info *sinfo,
+                                     struct sk_buff *skb)
+{
+       struct ieee80211_tx_rate *txrate;
+       struct ieee80211_tx_info *info;
+       struct _carl9170_tx_superframe *txc = (void *) skb->data;
+       int i;
+       bool ampdu;
+       bool no_ack;
+
+       info = IEEE80211_SKB_CB(skb);
+       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
+       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
+
+       /* Set the rate control probe flag for all (sub-) frames.
+        * This is because the TX_STATS_AMPDU flag is only set on
+        * the last frame, so it has to be inherited.
+        */
+       info->flags |= (sinfo->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE);
+
+       /* NOTE: For the first rate, the ERP & AMPDU flags are directly
+        * taken from mac_control. For all fallback rate, the firmware
+        * updates the mac_control flags from the rate info field.
+        */
+       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
+               __le32 phy_set;
+
+               txrate = &sinfo->control.rates[i];
+               if (txrate->idx < 0)
+                       break;
+
+               phy_set = carl9170_tx_physet(ar, info, txrate);
+               if (i == 0) {
+                       __le16 mac_tmp = cpu_to_le16(0);
+
+                       /* first rate - part of the hw's frame header */
+                       txc->f.phy_control = phy_set;
+
+                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+
+                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+                       else if (carl9170_tx_cts_check(ar, txrate))
+                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+
+                       txc->f.mac_control |= mac_tmp;
+               } else {
+                       /* fallback rates are stored in the firmware's
+                        * retry rate set array.
+                        */
+                       txc->s.rr[i - 1] = phy_set;
+               }
+
+               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
+                       txrate->count);
+
+               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+               else if (carl9170_tx_cts_check(ar, txrate))
+                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
+                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
+
+               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
+                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
+       }
+}
+
 static int carl9170_tx_prepare(struct ar9170 *ar,
                               struct ieee80211_sta *sta,
                               struct sk_buff *skb)
@@ -874,13 +961,10 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        struct _carl9170_tx_superframe *txc;
        struct carl9170_vif_info *cvif;
        struct ieee80211_tx_info *info;
-       struct ieee80211_tx_rate *txrate;
        struct carl9170_tx_info *arinfo;
        unsigned int hw_queue;
-       int i;
        __le16 mac_tmp;
        u16 len;
-       bool ampdu, no_ack;
 
        BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
        BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) !=
@@ -889,8 +973,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) !=
                     AR9170_TX_HWDESC_LEN);
 
-       BUILD_BUG_ON(IEEE80211_TX_MAX_RATES < CARL9170_TX_MAX_RATES);
-
        BUILD_BUG_ON(AR9170_MAX_VIRTUAL_MAC >
                ((CARL9170_TX_SUPER_MISC_VIF_ID >>
                 CARL9170_TX_SUPER_MISC_VIF_ID_S) + 1));
@@ -932,8 +1014,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
        mac_tmp |= cpu_to_le16((hw_queue << AR9170_TX_MAC_QOS_S) &
                               AR9170_TX_MAC_QOS);
 
-       no_ack = !!(info->flags & IEEE80211_TX_CTL_NO_ACK);
-       if (unlikely(no_ack))
+       if (unlikely(info->flags & IEEE80211_TX_CTL_NO_ACK))
                mac_tmp |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
 
        if (info->control.hw_key) {
@@ -954,8 +1035,7 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                }
        }
 
-       ampdu = !!(info->flags & IEEE80211_TX_CTL_AMPDU);
-       if (ampdu) {
+       if (info->flags & IEEE80211_TX_CTL_AMPDU) {
                unsigned int density, factor;
 
                if (unlikely(!sta || !cvif))
@@ -982,50 +1062,6 @@ static int carl9170_tx_prepare(struct ar9170 *ar,
                        txc->s.ampdu_settings, factor);
        }
 
-       /*
-        * NOTE: For the first rate, the ERP & AMPDU flags are directly
-        * taken from mac_control. For all fallback rate, the firmware
-        * updates the mac_control flags from the rate info field.
-        */
-       for (i = 0; i < CARL9170_TX_MAX_RATES; i++) {
-               __le32 phy_set;
-               txrate = &info->control.rates[i];
-               if (txrate->idx < 0)
-                       break;
-
-               phy_set = carl9170_tx_physet(ar, info, txrate);
-               if (i == 0) {
-                       /* first rate - part of the hw's frame header */
-                       txc->f.phy_control = phy_set;
-
-                       if (ampdu && txrate->flags & IEEE80211_TX_RC_MCS)
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_AGGR);
-                       if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
-                       else if (carl9170_tx_cts_check(ar, txrate))
-                               mac_tmp |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
-
-               } else {
-                       /* fallback rates are stored in the firmware's
-                        * retry rate set array.
-                        */
-                       txc->s.rr[i - 1] = phy_set;
-               }
-
-               SET_VAL(CARL9170_TX_SUPER_RI_TRIES, txc->s.ri[i],
-                       txrate->count);
-
-               if (carl9170_tx_rts_check(ar, txrate, ampdu, no_ack))
-                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_RTS <<
-                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
-               else if (carl9170_tx_cts_check(ar, txrate))
-                       txc->s.ri[i] |= (AR9170_TX_MAC_PROT_CTS <<
-                               CARL9170_TX_SUPER_RI_ERP_PROT_S);
-
-               if (ampdu && (txrate->flags & IEEE80211_TX_RC_MCS))
-                       txc->s.ri[i] |= CARL9170_TX_SUPER_RI_AMPDU;
-       }
-
        txc->s.len = cpu_to_le16(skb->len);
        txc->f.length = cpu_to_le16(len + FCS_LEN);
        txc->f.mac_control = mac_tmp;
@@ -1086,31 +1122,12 @@ static void carl9170_set_ampdu_params(struct ar9170 *ar, struct sk_buff *skb)
        }
 }
 
-static bool carl9170_tx_rate_check(struct ar9170 *ar, struct sk_buff *_dest,
-                                  struct sk_buff *_src)
-{
-       struct _carl9170_tx_superframe *dest, *src;
-
-       dest = (void *) _dest->data;
-       src = (void *) _src->data;
-
-       /*
-        * The mac80211 rate control algorithm expects that all MPDUs in
-        * an AMPDU share the same tx vectors.
-        * This is not really obvious right now, because the hardware
-        * does the AMPDU setup according to its own rulebook.
-        * Our nicely assembled, strictly monotonic increasing mpdu
-        * chains will be broken up, mashed back together...
-        */
-
-       return (dest->f.phy_control == src->f.phy_control);
-}
-
 static void carl9170_tx_ampdu(struct ar9170 *ar)
 {
        struct sk_buff_head agg;
        struct carl9170_sta_tid *tid_info;
        struct sk_buff *skb, *first;
+       struct ieee80211_tx_info *tx_info_first;
        unsigned int i = 0, done_ampdus = 0;
        u16 seq, queue, tmpssn;
 
@@ -1156,6 +1173,7 @@ retry:
                        goto processed;
                }
 
+               tx_info_first = NULL;
                while ((skb = skb_peek(&tid_info->queue))) {
                        /* strict 0, 1, ..., n - 1, n frame sequence order */
                        if (unlikely(carl9170_get_seq(skb) != seq))
@@ -1166,8 +1184,13 @@ retry:
                            (tid_info->max - 1)))
                                break;
 
-                       if (!carl9170_tx_rate_check(ar, skb, first))
-                               break;
+                       if (!tx_info_first) {
+                               carl9170_tx_get_rates(ar, tid_info->vif,
+                                                     tid_info->sta, first);
+                               tx_info_first = IEEE80211_SKB_CB(first);
+                       }
+
+                       carl9170_tx_apply_rateset(ar, tx_info_first, skb);
 
                        atomic_inc(&ar->tx_ampdu_upload);
                        tid_info->snx = seq = SEQ_NEXT(seq);
@@ -1182,8 +1205,7 @@ retry:
                if (skb_queue_empty(&tid_info->queue) ||
                    carl9170_get_seq(skb_peek(&tid_info->queue)) !=
                    tid_info->snx) {
-                       /*
-                        * stop TID, if A-MPDU frames are still missing,
+                       /* stop TID, if A-MPDU frames are still missing,
                         * or whenever the queue is empty.
                         */
 
@@ -1450,12 +1472,14 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
        struct ar9170 *ar = hw->priv;
        struct ieee80211_tx_info *info;
        struct ieee80211_sta *sta = control->sta;
+       struct ieee80211_vif *vif;
        bool run;
 
        if (unlikely(!IS_STARTED(ar)))
                goto err_free;
 
        info = IEEE80211_SKB_CB(skb);
+       vif = info->control.vif;
 
        if (unlikely(carl9170_tx_prepare(ar, sta, skb)))
                goto err_free;
@@ -1486,6 +1510,8 @@ void carl9170_op_tx(struct ieee80211_hw *hw,
        } else {
                unsigned int queue = skb_get_queue_mapping(skb);
 
+               carl9170_tx_get_rates(ar, vif, sta, skb);
+               carl9170_tx_apply_rateset(ar, info, skb);
                skb_queue_tail(&ar->tx_pending[queue], skb);
        }
 
index ccc4c71..7d077c7 100644 (file)
@@ -42,11 +42,11 @@ static int __ath_regd_init(struct ath_regulatory *reg);
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
 
 /* We allow IBSS on these on a case by case basis by regulatory domain */
-#define ATH9K_5GHZ_5150_5350   REG_RULE(5150-10, 5350+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5150_5350   REG_RULE(5150-10, 5350+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
-#define ATH9K_5GHZ_5470_5850   REG_RULE(5470-10, 5850+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5470_5850   REG_RULE(5470-10, 5850+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
-#define ATH9K_5GHZ_5725_5850   REG_RULE(5725-10, 5850+10, 40, 0, 30,\
+#define ATH9K_5GHZ_5725_5850   REG_RULE(5725-10, 5850+10, 80, 0, 30,\
                                NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
 
 #define ATH9K_2GHZ_ALL         ATH9K_2GHZ_CH01_11, \
index 5644ac5..ce8c038 100644 (file)
@@ -28,7 +28,7 @@ config WIL6210_ISR_COR
          such monitoring impossible.
          Say y unless you debug interrupts
 
-config ATH6KL_TRACING
+config WIL6210_TRACING
        bool "wil6210 tracing support"
        depends on WIL6210
        depends on EVENT_TRACING
index 4eb05d0..61c302a 100644 (file)
@@ -402,6 +402,30 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
        return 0;
 }
 
+static int wil_fix_bcon(struct wil6210_priv *wil,
+                       struct cfg80211_beacon_data *bcon)
+{
+       struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp;
+       size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       int rc = 0;
+
+       if (bcon->probe_resp_len <= hlen)
+               return 0;
+
+       if (!bcon->proberesp_ies) {
+               bcon->proberesp_ies = f->u.probe_resp.variable;
+               bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
+               rc = 1;
+       }
+       if (!bcon->assocresp_ies) {
+               bcon->assocresp_ies = f->u.probe_resp.variable;
+               bcon->assocresp_ies_len = bcon->probe_resp_len - hlen;
+               rc = 1;
+       }
+
+       return rc;
+}
+
 static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                                 struct net_device *ndev,
                                 struct cfg80211_ap_settings *info)
@@ -423,10 +447,18 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
 
+       if (wil_fix_bcon(wil, bcon))
+               wil_dbg_misc(wil, "Fixed bcon\n");
+
        rc = wil_reset(wil);
        if (rc)
                return rc;
 
+       /* Rx VRING. */
+       rc = wil_rx_init(wil);
+       if (rc)
+               return rc;
+
        rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
        if (rc)
                return rc;
@@ -455,8 +487,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        if (rc)
                return rc;
 
-       /* Rx VRING. After MAC and beacon */
-       rc = wil_rx_init(wil);
 
        netif_carrier_on(ndev);
 
index c97b864..0a2844c 100644 (file)
@@ -286,41 +286,36 @@ static int __wil_up(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
-       struct ieee80211_channel *channel = wdev->preset_chandef.chan;
        int rc;
-       int bi;
-       u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
 
        rc = wil_reset(wil);
        if (rc)
                return rc;
 
-       /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
-       wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
+       /* Rx VRING. After MAC and beacon */
+       rc = wil_rx_init(wil);
+       if (rc)
+               return rc;
+
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
                wil_dbg_misc(wil, "type: STATION\n");
-               bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_AP:
                wil_dbg_misc(wil, "type: AP\n");
-               bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
                wil_dbg_misc(wil, "type: P2P_CLIENT\n");
-               bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_GO:
                wil_dbg_misc(wil, "type: P2P_GO\n");
-               bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_MONITOR:
                wil_dbg_misc(wil, "type: Monitor\n");
-               bi = 0;
                ndev->type = ARPHRD_IEEE80211_RADIOTAP;
                /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
                break;
@@ -328,36 +323,9 @@ static int __wil_up(struct wil6210_priv *wil)
                return -EOPNOTSUPP;
        }
 
-       /* Apply profile in the following order: */
-       /* SSID and channel for the AP */
-       switch (wdev->iftype) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_P2P_GO:
-               if (wdev->ssid_len == 0) {
-                       wil_err(wil, "SSID not set\n");
-                       return -EINVAL;
-               }
-               rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
-               if (rc)
-                       return rc;
-               break;
-       default:
-               break;
-       }
-
        /* MAC address - pre-requisite for other commands */
        wmi_set_mac_address(wil, ndev->dev_addr);
 
-       /* Set up beaconing if required. */
-       if (bi > 0) {
-               rc = wmi_pcp_start(wil, bi, wmi_nettype,
-                                  (channel ? channel->hw_value : 0));
-               if (rc)
-                       return rc;
-       }
-
-       /* Rx VRING. After MAC and beacon */
-       wil_rx_init(wil);
 
        napi_enable(&wil->napi_rx);
        napi_enable(&wil->napi_tx);
index 00dffed..e1c492b 100644 (file)
@@ -768,18 +768,16 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                wil_err(wil, "Xmit in monitor mode not supported\n");
                goto drop;
        }
-       if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
-               rc = wmi_tx_eapol(wil, skb);
-       } else {
-               /* find vring */
-               vring = wil_find_tx_vring(wil, skb);
-               if (!vring) {
-                       wil_err(wil, "No Tx VRING available\n");
-                       goto drop;
-               }
-               /* set up vring entry */
-               rc = wil_tx_vring(wil, vring, skb);
+
+       /* find vring */
+       vring = wil_find_tx_vring(wil, skb);
+       if (!vring) {
+               wil_err(wil, "No Tx VRING available\n");
+               goto drop;
        }
+       /* set up vring entry */
+       rc = wil_tx_vring(wil, vring, skb);
+
        switch (rc) {
        case 0:
                /* statistics will be updated on the tx_complete */
index 373cf65..44fdab5 100644 (file)
@@ -329,7 +329,6 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
 int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
 int wmi_set_channel(struct wil6210_priv *wil, int channel);
 int wmi_get_channel(struct wil6210_priv *wil, int *channel);
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb);
 int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr);
 int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
index 527ffb5..dc8059a 100644 (file)
@@ -75,10 +75,11 @@ static const struct {
        {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
        {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
        {0x880000, 0x88a000, 0x880000}, /* various RGF */
-       {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */
+       {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
        /*
         * 920000..930000 ucode code RAM
         * 930000..932000 ucode data RAM
+        * 932000..949000 back-door debug data
         */
 };
 
@@ -314,8 +315,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
 
        wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n",
                    data->info.channel, data->info.mcs, data->info.snr);
-       wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
-                   le16_to_cpu(data->info.stype));
+       wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
+                   le16_to_cpu(fc));
        wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
                    data->info.qid, data->info.mid, data->info.cid);
 
@@ -739,8 +740,12 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
        if (!wil->secure_pcp)
                cmd.disable_sec = 1;
 
+       /*
+        * Processing time may be huge, in case of secure AP it takes about
+        * 3500ms for FW to start AP
+        */
        rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd),
-                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 100);
+                     WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000);
        if (rc)
                return rc;
 
@@ -834,40 +839,6 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel)
        return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd));
 }
 
-int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
-{
-       struct wmi_eapol_tx_cmd *cmd;
-       struct ethhdr *eth;
-       u16 eapol_len = skb->len - ETH_HLEN;
-       void *eapol = skb->data + ETH_HLEN;
-       uint i;
-       int rc;
-
-       skb_set_mac_header(skb, 0);
-       eth = eth_hdr(skb);
-       wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
-       for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
-               if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
-                       goto found_dest;
-       }
-
-       return -EINVAL;
-
- found_dest:
-       /* find out eapol data & len */
-       cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL);
-       if (!cmd)
-               return -EINVAL;
-
-       memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN);
-       cmd->eapol_len = cpu_to_le16(eapol_len);
-       memcpy(cmd->eapol, eapol, eapol_len);
-       rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len);
-       kfree(cmd);
-
-       return rc;
-}
-
 int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr)
 {
index 078e6f3..3f21e0b 100644 (file)
@@ -28,7 +28,7 @@ config B43
 
 config B43_BCMA
        bool "Support for BCMA bus"
-       depends on B43 && BCMA
+       depends on B43 && (BCMA = y || BCMA = B43)
        default y
 
 config B43_BCMA_EXTRA
@@ -39,7 +39,7 @@ config B43_BCMA_EXTRA
 
 config B43_SSB
        bool
-       depends on B43 && SSB
+       depends on B43 && (SSB = y || SSB = B43)
        default y
 
 # Auto-select SSB PCI-HOST support, if possible
@@ -111,6 +111,7 @@ config B43_PIO
 config B43_PHY_N
        bool "Support for 802.11n (N-PHY) devices"
        depends on B43
+       default y
        ---help---
          Support for the N-PHY.
 
@@ -132,6 +133,7 @@ config B43_PHY_LP
 config B43_PHY_HT
        bool "Support for HT-PHY (high throughput) devices"
        depends on B43 && B43_BCMA
+       default y
        ---help---
          Support for the HT-PHY.
 
index 4891e3d..3f8e69c 100644 (file)
 #include <linux/pci_ids.h>
 #include <linux/sched.h>
 #include <linux/completion.h>
+#include <linux/scatterlist.h>
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
 
 #include <defs.h>
@@ -303,6 +305,153 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
                *ret = retval;
 }
 
+/**
+ * brcmf_sdio_buffrw - SDIO interface function for block data access
+ * @sdiodev: brcmfmac sdio device
+ * @fn: SDIO function number
+ * @write: direction flag
+ * @addr: dongle memory address as source/destination
+ * @pkt: skb pointer
+ *
+ * This function takes the respbonsibility as the interface function to MMC
+ * stack for block data access. It assumes that the skb passed down by the
+ * caller has already been padded and aligned.
+ */
+static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                            bool write, u32 addr, struct sk_buff_head *pktlist)
+{
+       unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
+       unsigned int max_blks, max_req_sz;
+       unsigned short max_seg_sz, seg_sz;
+       unsigned char *pkt_data;
+       struct sk_buff *pkt_next = NULL;
+       struct mmc_request mmc_req;
+       struct mmc_command mmc_cmd;
+       struct mmc_data mmc_dat;
+       struct sg_table st;
+       struct scatterlist *sgl;
+       struct mmc_host *host;
+       int ret = 0;
+
+       if (!pktlist->qlen)
+               return -EINVAL;
+
+       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_pm_resume_error(sdiodev))
+               return -EIO;
+
+       /* Single skb use the standard mmc interface */
+       if (pktlist->qlen == 1) {
+               pkt_next = pktlist->next;
+               req_sz = pkt_next->len + 3;
+               req_sz &= (uint)~3;
+
+               if (write)
+                       return sdio_memcpy_toio(sdiodev->func[fn], addr,
+                                               ((u8 *)(pkt_next->data)),
+                                               req_sz);
+               else if (fn == 1)
+                       return sdio_memcpy_fromio(sdiodev->func[fn],
+                                                 ((u8 *)(pkt_next->data)),
+                                                 addr, req_sz);
+               else
+                       /* function 2 read is FIFO operation */
+                       return sdio_readsb(sdiodev->func[fn],
+                                          ((u8 *)(pkt_next->data)), addr,
+                                          req_sz);
+       }
+
+       host = sdiodev->func[fn]->card->host;
+       func_blk_sz = sdiodev->func[fn]->cur_blksize;
+       /* Blocks per command is limited by host count, host transfer
+        * size and the maximum for IO_RW_EXTENDED of 511 blocks.
+        */
+       max_blks = min_t(unsigned int, host->max_blk_count, 511u);
+       max_req_sz = min_t(unsigned int, host->max_req_size,
+                          max_blks * func_blk_sz);
+       max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC);
+       max_seg_sz = min_t(unsigned short, max_seg_sz, pktlist->qlen);
+       seg_sz = pktlist->qlen;
+       pkt_offset = 0;
+       pkt_next = pktlist->next;
+
+       if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL))
+               return -ENOMEM;
+
+       while (seg_sz) {
+               req_sz = 0;
+               sg_cnt = 0;
+               memset(&mmc_req, 0, sizeof(struct mmc_request));
+               memset(&mmc_cmd, 0, sizeof(struct mmc_command));
+               memset(&mmc_dat, 0, sizeof(struct mmc_data));
+               sgl = st.sgl;
+               /* prep sg table */
+               while (pkt_next != (struct sk_buff *)pktlist) {
+                       pkt_data = pkt_next->data + pkt_offset;
+                       sg_data_sz = pkt_next->len - pkt_offset;
+                       if (sg_data_sz > host->max_seg_size)
+                               sg_data_sz = host->max_seg_size;
+                       if (sg_data_sz > max_req_sz - req_sz)
+                               sg_data_sz = max_req_sz - req_sz;
+
+                       sg_set_buf(sgl, pkt_data, sg_data_sz);
+
+                       sg_cnt++;
+                       sgl = sg_next(sgl);
+                       req_sz += sg_data_sz;
+                       pkt_offset += sg_data_sz;
+                       if (pkt_offset == pkt_next->len) {
+                               pkt_offset = 0;
+                               pkt_next = pkt_next->next;
+                       }
+
+                       if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz)
+                               break;
+               }
+               seg_sz -= sg_cnt;
+
+               if (req_sz % func_blk_sz != 0) {
+                       brcmf_err("sg request length %u is not %u aligned\n",
+                                 req_sz, func_blk_sz);
+                       sg_free_table(&st);
+                       return -ENOTBLK;
+               }
+               mmc_dat.sg = st.sgl;
+               mmc_dat.sg_len = sg_cnt;
+               mmc_dat.blksz = func_blk_sz;
+               mmc_dat.blocks = req_sz / func_blk_sz;
+               mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+               mmc_cmd.opcode = SD_IO_RW_EXTENDED;
+               mmc_cmd.arg = write ? 1<<31 : 0;        /* write flag  */
+               mmc_cmd.arg |= (fn & 0x7) << 28;        /* SDIO func num */
+               mmc_cmd.arg |= 1<<27;                   /* block mode */
+               /* incrementing addr for function 1 */
+               mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0;
+               mmc_cmd.arg |= (addr & 0x1FFFF) << 9;   /* address */
+               mmc_cmd.arg |= mmc_dat.blocks & 0x1FF;  /* block count */
+               mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+               mmc_req.cmd = &mmc_cmd;
+               mmc_req.data = &mmc_dat;
+               if (fn == 1)
+                       addr += req_sz;
+
+               mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card);
+               mmc_wait_for_req(host, &mmc_req);
+
+               ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error;
+               if (ret != 0) {
+                       brcmf_err("CMD53 sg block %s failed %d\n",
+                                 write ? "write" : "read", ret);
+                       ret = -EIO;
+                       break;
+               }
+       }
+
+       sg_free_table(&st);
+
+       return ret;
+}
+
 static int brcmf_sdcard_recv_prepare(struct brcmf_sdio_dev *sdiodev, uint fn,
                                     uint flags, uint width, u32 *addr)
 {
@@ -355,9 +504,9 @@ int
 brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff *pkt)
 {
-       uint incr_fix;
        uint width;
        int err = 0;
+       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
@@ -367,9 +516,10 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        if (err)
                goto done;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ,
-                                        fn, addr, pkt);
+       skb_queue_head_init(&pkt_list);
+       skb_queue_tail(&pkt_list, pkt);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list);
+       skb_dequeue_tail(&pkt_list);
 
 done:
        return err;
@@ -391,8 +541,7 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                goto done;
 
        incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
-       err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr,
-                                       pktq);
+       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq);
 
 done:
        return err;
@@ -424,10 +573,10 @@ int
 brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                      uint flags, struct sk_buff *pkt)
 {
-       uint incr_fix;
        uint width;
        uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
        int err = 0;
+       struct sk_buff_head pkt_list;
 
        brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
                  fn, addr, pkt->len);
@@ -446,13 +595,14 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 
        addr &= SBSDIO_SB_OFT_ADDR_MASK;
 
-       incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
        width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
        if (width == 4)
                addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
 
-       err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn,
-                                        addr, pkt);
+       skb_queue_head_init(&pkt_list);
+       skb_queue_tail(&pkt_list, pkt);
+       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list);
+       skb_dequeue_tail(&pkt_list);
 
 done:
        return err;
@@ -466,6 +616,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        struct sk_buff *pkt;
        u32 sdaddr;
        uint dsize;
+       struct sk_buff_head pkt_list;
 
        dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
        pkt = dev_alloc_skb(dsize);
@@ -474,6 +625,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                return -EIO;
        }
        pkt->priority = 0;
+       skb_queue_head_init(&pkt_list);
 
        /* Determine initial transfer parameters */
        sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
@@ -501,9 +653,10 @@ 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_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC,
-                                                     write, SDIO_FUNC_1,
-                                                     sdaddr, pkt);
+               skb_queue_tail(&pkt_list, pkt);
+               bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
+                                            sdaddr, &pkt_list);
+               skb_dequeue_tail(&pkt_list);
                if (bcmerror) {
                        brcmf_err("membytes transfer failed\n");
                        break;
index 11400b3..289e386 100644 (file)
@@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
 static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
 
 
-static bool
+bool
 brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
 {
        bool is_err = false;
@@ -76,7 +76,7 @@ brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
        return is_err;
 }
 
-static void
+void
 brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq)
 {
 #ifdef CONFIG_PM_SLEEP
@@ -211,115 +211,6 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
        return err_ret;
 }
 
-/* precondition: host controller is claimed */
-static int
-brcmf_sdioh_request_data(struct brcmf_sdio_dev *sdiodev, uint write, bool fifo,
-                        uint func, uint addr, struct sk_buff *pkt, uint pktlen)
-{
-       int err_ret = 0;
-
-       if ((write) && (!fifo)) {
-               err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
-                                          ((u8 *) (pkt->data)), pktlen);
-       } else if (write) {
-               err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
-                                          ((u8 *) (pkt->data)), pktlen);
-       } else if (fifo) {
-               err_ret = sdio_readsb(sdiodev->func[func],
-                                     ((u8 *) (pkt->data)), addr, pktlen);
-       } else {
-               err_ret = sdio_memcpy_fromio(sdiodev->func[func],
-                                            ((u8 *) (pkt->data)),
-                                            addr, pktlen);
-       }
-
-       return err_ret;
-}
-
-/*
- * This function takes a queue of packets. The packets on the queue
- * are assumed to be properly aligned by the caller.
- */
-int
-brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
-                         uint write, uint func, uint addr,
-                         struct sk_buff_head *pktq)
-{
-       bool fifo = (fix_inc == SDIOH_DATA_FIX);
-       u32 SGCount = 0;
-       int err_ret = 0;
-
-       struct sk_buff *pkt;
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       skb_queue_walk(pktq, pkt) {
-               uint pkt_len = pkt->len;
-               pkt_len += 3;
-               pkt_len &= 0xFFFFFFFC;
-
-               err_ret = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
-                                                  addr, pkt, pkt_len);
-               if (err_ret) {
-                       brcmf_err("%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
-                                 write ? "TX" : "RX", pkt, SGCount, addr,
-                                 pkt_len, err_ret);
-               } else {
-                       brcmf_dbg(SDIO, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
-                                 write ? "TX" : "RX", pkt, SGCount, addr,
-                                 pkt_len);
-               }
-               if (!fifo)
-                       addr += pkt_len;
-
-               SGCount++;
-       }
-
-       brcmf_dbg(SDIO, "Exit\n");
-       return err_ret;
-}
-
-/*
- * This function takes a single DMA-able packet.
- */
-int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
-                              uint fix_inc, uint write, uint func, uint addr,
-                              struct sk_buff *pkt)
-{
-       int status;
-       uint pkt_len;
-       bool fifo = (fix_inc == SDIOH_DATA_FIX);
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       if (pkt == NULL)
-               return -EINVAL;
-       pkt_len = pkt->len;
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       pkt_len += 3;
-       pkt_len &= (uint)~3;
-
-       status = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
-                                          addr, pkt, pkt_len);
-       if (status) {
-               brcmf_err("%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
-                         write ? "TX" : "RX", pkt, addr, pkt_len, status);
-       } else {
-               brcmf_dbg(SDIO, "%s xfr'd %p, addr=0x%05x, len=%d\n",
-                         write ? "TX" : "RX", pkt, addr, pkt_len);
-       }
-
-       return status;
-}
-
 static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr)
 {
        /* read 24 bits and return valid 17 bit addr */
@@ -468,7 +359,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
        atomic_set(&sdiodev->suspend, false);
        init_waitqueue_head(&sdiodev->request_byte_wait);
        init_waitqueue_head(&sdiodev->request_word_wait);
-       init_waitqueue_head(&sdiodev->request_chain_wait);
        init_waitqueue_head(&sdiodev->request_buffer_wait);
 
        brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n");
index 28db9cf..86cbfe2 100644 (file)
@@ -583,6 +583,7 @@ enum brcmf_netif_stop_reason {
  * @bssidx: index of bss associated with this interface.
  * @mac_addr: assigned mac address.
  * @netif_stop: bitmap indicates reason why netif queues are stopped.
+ * @netif_stop_lock: spinlock for update netif_stop from multiple sources.
  * @pend_8021x_cnt: tracks outstanding number of 802.1x frames.
  * @pend_8021x_wait: used for signalling change in count.
  */
@@ -598,6 +599,7 @@ struct brcmf_if {
        s32 bssidx;
        u8 mac_addr[ETH_ALEN];
        u8 netif_stop;
+       spinlock_t netif_stop_lock;
        atomic_t pend_8021x_cnt;
        wait_queue_head_t pend_8021x_wait;
 };
index 59c77aa..dd85401 100644 (file)
@@ -30,6 +30,7 @@
 #include "dhd_bus.h"
 #include "fwsignal.h"
 #include "dhd_dbg.h"
+#include "tracepoint.h"
 
 struct brcmf_proto_cdc_dcmd {
        __le32 cmd;     /* dongle command value */
@@ -292,6 +293,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset,
        h->flags2 = 0;
        h->data_offset = offset;
        BDC_SET_IF_IDX(h, ifidx);
+       trace_brcmf_bdchdr(pktbuf->data);
 }
 
 int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
@@ -309,6 +311,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
                return -EBADE;
        }
 
+       trace_brcmf_bdchdr(pktbuf->data);
        h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
 
        *ifidx = BDC_GET_IF_IDX(h);
index 202869c..c37b9d6 100644 (file)
@@ -156,8 +156,11 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
                        "txs_suppr_core:    %u\n"
                        "txs_suppr_ps:      %u\n"
                        "txs_tossed:        %u\n"
+                       "txs_host_tossed:   %u\n"
+                       "bus_flow_block:    %u\n"
+                       "fws_flow_block:    %u\n"
                        "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
-                       "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+                       "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
                        fwstats->header_pulls,
                        fwstats->header_only_pkt,
                        fwstats->tlv_parse_failed,
@@ -176,14 +179,17 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
                        fwstats->txs_supp_core,
                        fwstats->txs_supp_ps,
                        fwstats->txs_tossed,
+                       fwstats->txs_host_tossed,
+                       fwstats->bus_flow_block,
+                       fwstats->fws_flow_block,
                        fwstats->send_pkts[0], fwstats->send_pkts[1],
                        fwstats->send_pkts[2], fwstats->send_pkts[3],
                        fwstats->send_pkts[4],
-                       fwstats->fifo_credits_sent[0],
-                       fwstats->fifo_credits_sent[1],
-                       fwstats->fifo_credits_sent[2],
-                       fwstats->fifo_credits_sent[3],
-                       fwstats->fifo_credits_sent[4]);
+                       fwstats->requested_sent[0],
+                       fwstats->requested_sent[1],
+                       fwstats->requested_sent[2],
+                       fwstats->requested_sent[3],
+                       fwstats->requested_sent[4]);
 
        return simple_read_from_buffer(data, count, ppos, buf, res);
 }
index 009c87b..0af1f5d 100644 (file)
@@ -141,8 +141,7 @@ struct brcmf_fws_stats {
        u32 header_pulls;
        u32 pkt2bus;
        u32 send_pkts[5];
-       u32 fifo_credits_sent[5];
-       u32 fifo_credits_back[6];
+       u32 requested_sent[5];
        u32 generic_error;
        u32 mac_update_failed;
        u32 mac_ps_update_failed;
@@ -158,6 +157,9 @@ struct brcmf_fws_stats {
        u32 txs_supp_core;
        u32 txs_supp_ps;
        u32 txs_tossed;
+       u32 txs_host_tossed;
+       u32 bus_flow_block;
+       u32 fws_flow_block;
 };
 
 struct brcmf_pub;
index 2c59357..8c402e7 100644 (file)
@@ -179,7 +179,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
        struct brcmf_pub *drvr = ifp->drvr;
        struct ethhdr *eh;
 
-       brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+       brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
 
        /* Can the device send data? */
        if (drvr->bus_if->state != BRCMF_BUS_DATA) {
@@ -240,11 +240,15 @@ done:
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state)
 {
+       unsigned long flags;
+
        if (!ifp)
                return;
 
        brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
                  ifp->bssidx, ifp->netif_stop, reason, state);
+
+       spin_lock_irqsave(&ifp->netif_stop_lock, flags);
        if (state) {
                if (!ifp->netif_stop)
                        netif_stop_queue(ifp->ndev);
@@ -254,6 +258,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp,
                if (!ifp->netif_stop)
                        netif_wake_queue(ifp->ndev);
        }
+       spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
 }
 
 void brcmf_txflowblock(struct device *dev, bool state)
@@ -264,9 +269,14 @@ void brcmf_txflowblock(struct device *dev, bool state)
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       for (i = 0; i < BRCMF_MAX_IFS; i++)
-               brcmf_txflowblock_if(drvr->iflist[i],
-                                    BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state);
+       if (brcmf_fws_fc_active(drvr->fws)) {
+               brcmf_fws_bus_blocked(drvr, state);
+       } else {
+               for (i = 0; i < BRCMF_MAX_IFS; i++)
+                       brcmf_txflowblock_if(drvr->iflist[i],
+                                            BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
+                                            state);
+       }
 }
 
 void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
@@ -280,7 +290,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
        u8 ifidx;
        int ret;
 
-       brcmf_dbg(TRACE, "Enter\n");
+       brcmf_dbg(DATA, "Enter\n");
 
        skb_queue_walk_safe(skb_list, skb, pnext) {
                skb_unlink(skb, skb_list);
@@ -630,7 +640,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
        /* set appropriate operations */
        ndev->netdev_ops = &brcmf_netdev_ops_pri;
 
-       ndev->hard_header_len = ETH_HLEN + drvr->hdrlen;
+       ndev->hard_header_len += drvr->hdrlen;
        ndev->ethtool_ops = &brcmf_ethtool_ops;
 
        drvr->rxsz = ndev->mtu + ndev->hard_header_len +
@@ -779,6 +789,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
        ifp->bssidx = bssidx;
 
        init_waitqueue_head(&ifp->pend_8021x_wait);
+       spin_lock_init(&ifp->netif_stop_lock);
 
        if (mac_addr != NULL)
                memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
index d248751..2641119 100644 (file)
@@ -448,8 +448,6 @@ struct brcmf_sdio {
        uint rxblen;            /* Allocated length of rxbuf */
        u8 *rxctl;              /* Aligned pointer into rxbuf */
        u8 *rxctl_orig;         /* pointer for freeing rxctl */
-       u8 *databuf;            /* Buffer for receiving big glom packet */
-       u8 *dataptr;            /* Aligned pointer into databuf */
        uint rxlen;             /* Length of valid data in buffer */
        spinlock_t rxctl_lock;  /* protection lock for ctrl frame resources */
 
@@ -473,8 +471,6 @@ struct brcmf_sdio {
        s32 idletime;           /* Control for activity timeout */
        s32 idlecount;  /* Activity timeout counter */
        s32 idleclock;  /* How to set bus driver when idle */
-       s32 sd_rxchain;
-       bool use_rxchain;       /* If brcmf should use PKT chains */
        bool rxflow_mode;       /* Rx flow control mode */
        bool rxflow;            /* Is rx flow control on */
        bool alp_only;          /* Don't use HT clock (ALP only) */
@@ -495,8 +491,7 @@ struct brcmf_sdio {
 
        struct workqueue_struct *brcmf_wq;
        struct work_struct datawork;
-       struct list_head dpc_tsklst;
-       spinlock_t dpc_tl_lock;
+       atomic_t dpc_tskcnt;
 
        const struct firmware *firmware;
        u32 fw_ptr;
@@ -1026,29 +1021,6 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
                bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
 }
 
-/* copy a buffer into a pkt buffer chain */
-static uint brcmf_sdbrcm_glom_from_buf(struct brcmf_sdio *bus, uint len)
-{
-       uint n, ret = 0;
-       struct sk_buff *p;
-       u8 *buf;
-
-       buf = bus->dataptr;
-
-       /* copy the data */
-       skb_queue_walk(&bus->glom, p) {
-               n = min_t(uint, p->len, len);
-               memcpy(p->data, buf, n);
-               buf += n;
-               len -= n;
-               ret += n;
-               if (!len)
-                       break;
-       }
-
-       return ret;
-}
-
 /* return total length of buffer chain */
 static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)
 {
@@ -1202,8 +1174,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        int errcode;
        u8 doff, sfdoff;
 
-       bool usechain = bus->use_rxchain;
-
        struct brcmf_sdio_read rd_new;
 
        /* If packets, issue read(s) and send up packet chain */
@@ -1238,7 +1208,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        if (sublen % BRCMF_SDALIGN) {
                                brcmf_err("sublen %d not multiple of %d\n",
                                          sublen, BRCMF_SDALIGN);
-                               usechain = false;
                        }
                        totlen += sublen;
 
@@ -1305,27 +1274,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                 * packet and and copy into the chain.
                 */
                sdio_claim_host(bus->sdiodev->func[1]);
-               if (usechain) {
-                       errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
-                                       bus->sdiodev->sbwad,
-                                       SDIO_FUNC_2, F2SYNC, &bus->glom);
-               } else if (bus->dataptr) {
-                       errcode = brcmf_sdcard_recv_buf(bus->sdiodev,
-                                       bus->sdiodev->sbwad,
-                                       SDIO_FUNC_2, F2SYNC,
-                                       bus->dataptr, dlen);
-                       sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen);
-                       if (sublen != dlen) {
-                               brcmf_err("FAILED TO COPY, dlen %d sublen %d\n",
-                                         dlen, sublen);
-                               errcode = -1;
-                       }
-                       pnext = NULL;
-               } else {
-                       brcmf_err("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
-                                 dlen);
-                       errcode = -1;
-               }
+               errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
+                               bus->sdiodev->sbwad,
+                               SDIO_FUNC_2, F2SYNC, &bus->glom);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->sdcnt.f2rxdata++;
 
@@ -2061,23 +2012,6 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
        }
 }
 
-static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
-{
-       struct list_head *new_hd;
-       unsigned long flags;
-
-       if (in_interrupt())
-               new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
-       else
-               new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
-       if (new_hd == NULL)
-               return;
-
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       list_add_tail(new_hd, &bus->dpc_tsklst);
-       spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-}
-
 static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
 {
        u8 idx;
@@ -2312,7 +2246,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                   (!atomic_read(&bus->fcstate) &&
                    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
                    data_ok(bus)) || PKT_AVAILABLE()) {
-               brcmf_sdbrcm_adddpctsk(bus);
+               atomic_inc(&bus->dpc_tskcnt);
        }
 
        /* If we're done for now, turn off clock request. */
@@ -2342,7 +2276,6 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        struct brcmf_sdio *bus = sdiodev->bus;
-       unsigned long flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -2369,26 +2302,21 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
        } else {
                ret = 0;
        }
-       spin_unlock_bh(&bus->txqlock);
 
        if (pktq_len(&bus->txq) >= TXHI) {
                bus->txoff = true;
                brcmf_txflowblock(bus->sdiodev->dev, true);
        }
+       spin_unlock_bh(&bus->txqlock);
 
 #ifdef DEBUG
        if (pktq_plen(&bus->txq, prec) > qcount[prec])
                qcount[prec] = pktq_plen(&bus->txq, prec);
 #endif
 
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       if (list_empty(&bus->dpc_tsklst)) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
-               brcmf_sdbrcm_adddpctsk(bus);
+       if (atomic_read(&bus->dpc_tskcnt) == 0) {
+               atomic_inc(&bus->dpc_tskcnt);
                queue_work(bus->brcmf_wq, &bus->datawork);
-       } else {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
        }
 
        return ret;
@@ -2525,7 +2453,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
        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 flags;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -2612,18 +2539,13 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
                } while (ret < 0 && retries++ < TXRETRIES);
        }
 
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
        if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) &&
-           list_empty(&bus->dpc_tsklst)) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
-
+           atomic_read(&bus->dpc_tskcnt) == 0) {
                bus->activity = false;
                sdio_claim_host(bus->sdiodev->func[1]);
                brcmf_dbg(INFO, "idle\n");
                brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
                sdio_release_host(bus->sdiodev->func[1]);
-       } else {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
        }
 
        if (ret)
@@ -3451,7 +3373,7 @@ void brcmf_sdbrcm_isr(void *arg)
        if (!bus->intr)
                brcmf_err("isr w/o interrupt configured!\n");
 
-       brcmf_sdbrcm_adddpctsk(bus);
+       atomic_inc(&bus->dpc_tskcnt);
        queue_work(bus->brcmf_wq, &bus->datawork);
 }
 
@@ -3460,7 +3382,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
 #ifdef DEBUG
        struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
 #endif /* DEBUG */
-       unsigned long flags;
 
        brcmf_dbg(TIMER, "Enter\n");
 
@@ -3476,11 +3397,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                if (!bus->intr ||
                    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
 
-                       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-                       if (list_empty(&bus->dpc_tsklst)) {
+                       if (atomic_read(&bus->dpc_tskcnt) == 0) {
                                u8 devpend;
-                               spin_unlock_irqrestore(&bus->dpc_tl_lock,
-                                                      flags);
+
                                sdio_claim_host(bus->sdiodev->func[1]);
                                devpend = brcmf_sdio_regrb(bus->sdiodev,
                                                           SDIO_CCCR_INTx,
@@ -3489,9 +3408,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                intstatus =
                                    devpend & (INTR_STATUS_FUNC1 |
                                               INTR_STATUS_FUNC2);
-                       } else {
-                               spin_unlock_irqrestore(&bus->dpc_tl_lock,
-                                                      flags);
                        }
 
                        /* If there is something, make like the ISR and
@@ -3500,7 +3416,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                bus->sdcnt.pollcnt++;
                                atomic_set(&bus->ipend, 1);
 
-                               brcmf_sdbrcm_adddpctsk(bus);
+                               atomic_inc(&bus->dpc_tskcnt);
                                queue_work(bus->brcmf_wq, &bus->datawork);
                        }
                }
@@ -3545,41 +3461,15 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
        return (atomic_read(&bus->ipend) > 0);
 }
 
-static bool brcmf_sdbrcm_chipmatch(u16 chipid)
-{
-       if (chipid == BCM43143_CHIP_ID)
-               return true;
-       if (chipid == BCM43241_CHIP_ID)
-               return true;
-       if (chipid == BCM4329_CHIP_ID)
-               return true;
-       if (chipid == BCM4330_CHIP_ID)
-               return true;
-       if (chipid == BCM4334_CHIP_ID)
-               return true;
-       if (chipid == BCM4335_CHIP_ID)
-               return true;
-       return false;
-}
-
 static void brcmf_sdio_dataworker(struct work_struct *work)
 {
        struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
                                              datawork);
-       struct list_head *cur_hd, *tmp_hd;
-       unsigned long flags;
-
-       spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-       list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
-               spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 
+       while (atomic_read(&bus->dpc_tskcnt)) {
                brcmf_sdbrcm_dpc(bus);
-
-               spin_lock_irqsave(&bus->dpc_tl_lock, flags);
-               list_del(cur_hd);
-               kfree(cur_hd);
+               atomic_dec(&bus->dpc_tskcnt);
        }
-       spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
 }
 
 static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
@@ -3589,9 +3479,6 @@ static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
        kfree(bus->rxbuf);
        bus->rxctl = bus->rxbuf = NULL;
        bus->rxlen = 0;
-
-       kfree(bus->databuf);
-       bus->databuf = NULL;
 }
 
 static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
@@ -3604,29 +3491,10 @@ static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
                            ALIGNMENT) + BRCMF_SDALIGN;
                bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
                if (!(bus->rxbuf))
-                       goto fail;
-       }
-
-       /* Allocate buffer to receive glomed packet */
-       bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
-       if (!(bus->databuf)) {
-               /* release rxbuf which was already located as above */
-               if (!bus->rxblen)
-                       kfree(bus->rxbuf);
-               goto fail;
+                       return false;
        }
 
-       /* Align the buffer */
-       if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
-               bus->dataptr = bus->databuf + (BRCMF_SDALIGN -
-                              ((unsigned long)bus->databuf % BRCMF_SDALIGN));
-       else
-               bus->dataptr = bus->databuf;
-
        return true;
-
-fail:
-       return false;
 }
 
 static bool
@@ -3667,11 +3535,6 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
                goto fail;
        }
 
-       if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
-               brcmf_err("unsupported chip: 0x%04x\n", bus->ci->chip);
-               goto fail;
-       }
-
        if (brcmf_sdbrcm_kso_init(bus)) {
                brcmf_err("error enabling KSO\n");
                goto fail;
@@ -3770,10 +3633,6 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus)
        bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
        bus->roundup = min(max_roundup, bus->blocksize);
 
-       /* bus module does not support packet chaining */
-       bus->use_rxchain = false;
-       bus->sd_rxchain = false;
-
        /* SR state */
        bus->sleeping = false;
        bus->sr_enabled = false;
@@ -3927,8 +3786,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
                bus->watchdog_tsk = NULL;
        }
        /* Initialize DPC thread */
-       INIT_LIST_HEAD(&bus->dpc_tsklst);
-       spin_lock_init(&bus->dpc_tl_lock);
+       atomic_set(&bus->dpc_tskcnt, 0);
 
        /* Assign bus interface call back */
        bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
index 6ec5db9..e679214 100644 (file)
@@ -101,7 +101,8 @@ struct brcmf_event;
        BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \
        BRCMF_ENUM_DEF(DCS_REQUEST, 73) \
        BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
-       BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75)
+       BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
+       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
 
 #define BRCMF_ENUM_DEF(id, val) \
        BRCMF_E_##id = (val),
index 5352dc1..13e75c4 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/etherdevice.h>
 #include <linux/err.h>
 #include <linux/jiffies.h>
-#include <uapi/linux/nl80211.h>
 #include <net/cfg80211.h>
 
 #include <brcmu_utils.h>
@@ -142,7 +141,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
 #define BRCMF_FWS_FLOWCONTROL_HIWATER                  128
 #define BRCMF_FWS_FLOWCONTROL_LOWATER                  64
 
-#define BRCMF_FWS_PSQ_PREC_COUNT               ((NL80211_NUM_ACS + 1) * 2)
+#define BRCMF_FWS_PSQ_PREC_COUNT               ((BRCMF_FWS_FIFO_COUNT + 1) * 2)
 #define BRCMF_FWS_PSQ_LEN                              256
 
 #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST                        0x01
@@ -157,11 +156,13 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
  * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver.
  * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue.
  * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware.
+ * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info.
  */
 enum brcmf_fws_skb_state {
        BRCMF_FWS_SKBSTATE_NEW,
        BRCMF_FWS_SKBSTATE_DELAYED,
-       BRCMF_FWS_SKBSTATE_SUPPRESSED
+       BRCMF_FWS_SKBSTATE_SUPPRESSED,
+       BRCMF_FWS_SKBSTATE_TIM
 };
 
 /**
@@ -193,9 +194,8 @@ struct brcmf_skbuff_cb {
  *     b[11]  - packet sent upon firmware request.
  *     b[10]  - packet only contains signalling data.
  *     b[9]   - packet is a tx packet.
- *     b[8]   - packet uses FIFO credit (non-pspoll).
+ *     b[8]   - packet used requested credit
  *     b[7]   - interface in AP mode.
- *     b[6:4] - AC FIFO number.
  *     b[3:0] - interface index.
  */
 #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK      0x0800
@@ -204,12 +204,10 @@ struct brcmf_skbuff_cb {
 #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT   10
 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK        0x0200
 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT      9
-#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK    0x0100
-#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT   8
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK     0x0100
+#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT    8
 #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK          0x0080
 #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT         7
-#define BRCMF_SKB_IF_FLAGS_FIFO_MASK           0x0070
-#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT          4
 #define BRCMF_SKB_IF_FLAGS_INDEX_MASK          0x000f
 #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT         0
 
@@ -246,7 +244,7 @@ struct brcmf_skbuff_cb {
 #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK                  0x00ffff00
 #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT                 8
 #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK                        0x000000ff
-#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT                       0
+#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT               0
 
 #define brcmf_skb_htod_tag_set_field(skb, field, value) \
        brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \
@@ -278,6 +276,7 @@ struct brcmf_skbuff_cb {
 /**
  * enum brcmf_fws_fifo - fifo indices used by dongle firmware.
  *
+ * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
  * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
  * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
  * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
@@ -287,7 +286,8 @@ struct brcmf_skbuff_cb {
  * @BRCMF_FWS_FIFO_COUNT: number of fifos.
  */
 enum brcmf_fws_fifo {
-       BRCMF_FWS_FIFO_AC_BK,
+       BRCMF_FWS_FIFO_FIRST,
+       BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
        BRCMF_FWS_FIFO_AC_BE,
        BRCMF_FWS_FIFO_AC_VI,
        BRCMF_FWS_FIFO_AC_VO,
@@ -307,12 +307,15 @@ enum brcmf_fws_fifo {
  *     firmware suppress the packet as device is already in PS mode.
  * @BRCMF_FWS_TXSTATUS_FW_TOSSED:
  *     firmware tossed the packet.
+ * @BRCMF_FWS_TXSTATUS_HOST_TOSSED:
+ *     host tossed the packet.
  */
 enum brcmf_fws_txstatus {
        BRCMF_FWS_TXSTATUS_DISCARD,
        BRCMF_FWS_TXSTATUS_CORE_SUPPRESS,
        BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS,
-       BRCMF_FWS_TXSTATUS_FW_TOSSED
+       BRCMF_FWS_TXSTATUS_FW_TOSSED,
+       BRCMF_FWS_TXSTATUS_HOST_TOSSED
 };
 
 enum brcmf_fws_fcmode {
@@ -343,6 +346,7 @@ enum brcmf_fws_mac_desc_state {
  * @transit_count: packet in transit to firmware.
  */
 struct brcmf_fws_mac_descriptor {
+       char name[16];
        u8 occupied;
        u8 mac_handle;
        u8 interface_id;
@@ -356,7 +360,6 @@ struct brcmf_fws_mac_descriptor {
        u8 seq[BRCMF_FWS_FIFO_COUNT];
        struct pktq psq;
        int transit_count;
-       int suppress_count;
        int suppr_transit_count;
        bool send_tim_signal;
        u8 traffic_pending_bmp;
@@ -383,12 +386,10 @@ enum brcmf_fws_hanger_item_state {
  * struct brcmf_fws_hanger_item - single entry for tx pending packet.
  *
  * @state: entry is either free or occupied.
- * @gen: generation.
  * @pkt: packet itself.
  */
 struct brcmf_fws_hanger_item {
        enum brcmf_fws_hanger_item_state state;
-       u8 gen;
        struct sk_buff *pkt;
 };
 
@@ -424,6 +425,7 @@ struct brcmf_fws_info {
        struct brcmf_fws_stats stats;
        struct brcmf_fws_hanger hanger;
        enum brcmf_fws_fcmode fcmode;
+       bool bcmc_credit_check;
        struct brcmf_fws_macdesc_table desc;
        struct workqueue_struct *fws_wq;
        struct work_struct fws_dequeue_work;
@@ -434,6 +436,8 @@ struct brcmf_fws_info {
        u32 fifo_credit_map;
        u32 fifo_delay_map;
        unsigned long borrow_defer_timestamp;
+       bool bus_flow_blocked;
+       bool creditmap_received;
 };
 
 /*
@@ -507,7 +511,6 @@ static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger)
 {
        int i;
 
-       brcmf_dbg(TRACE, "enter\n");
        memset(hanger, 0, sizeof(*hanger));
        for (i = 0; i < ARRAY_SIZE(hanger->items); i++)
                hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
@@ -517,7 +520,6 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
 {
        u32 i;
 
-       brcmf_dbg(TRACE, "enter\n");
        i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS;
 
        while (i != h->slot_pos) {
@@ -533,14 +535,12 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h)
        h->failed_slotfind++;
        i = BRCMF_FWS_HANGER_MAXITEMS;
 done:
-       brcmf_dbg(TRACE, "exit: %d\n", i);
        return i;
 }
 
 static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h,
-                                          struct sk_buff *pkt, u32 slot_id)
+                                   struct sk_buff *pkt, u32 slot_id)
 {
-       brcmf_dbg(TRACE, "enter\n");
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
@@ -560,7 +560,6 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
                                          u32 slot_id, struct sk_buff **pktout,
                                          bool remove_item)
 {
-       brcmf_dbg(TRACE, "enter\n");
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
@@ -574,23 +573,18 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
        if (remove_item) {
                h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
                h->items[slot_id].pkt = NULL;
-               h->items[slot_id].gen = 0xff;
                h->popped++;
        }
        return 0;
 }
 
 static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
-                                                  u32 slot_id, u8 gen)
+                                           u32 slot_id)
 {
-       brcmf_dbg(TRACE, "enter\n");
-
        if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
                return -ENOENT;
 
-       h->items[slot_id].gen = gen;
-
-       if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) {
+       if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
                brcmf_err("entry not in use\n");
                return -EINVAL;
        }
@@ -599,25 +593,6 @@ static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h,
        return 0;
 }
 
-static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger,
-                                             struct sk_buff *pkt, u32 slot_id,
-                                             int *gen)
-{
-       brcmf_dbg(TRACE, "enter\n");
-       *gen = 0xff;
-
-       if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS)
-               return -ENOENT;
-
-       if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) {
-               brcmf_err("slot not in use\n");
-               return -EINVAL;
-       }
-
-       *gen = hanger->items[slot_id].gen;
-       return 0;
-}
-
 static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
                                     bool (*fn)(struct sk_buff *, void *),
                                     int ifidx)
@@ -627,7 +602,6 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
        int i;
        enum brcmf_fws_hanger_item_state s;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        for (i = 0; i < ARRAY_SIZE(h->items); i++) {
                s = h->items[i].state;
                if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE ||
@@ -644,14 +618,28 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws,
        }
 }
 
-static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
-                                         u8 *addr, u8 ifidx)
+static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws,
+                                      struct brcmf_fws_mac_descriptor *desc)
+{
+       if (desc == &fws->desc.other)
+               strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name));
+       else if (desc->mac_handle)
+               scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d",
+                         desc->mac_handle, desc->interface_id);
+       else
+               scnprintf(desc->name, sizeof(desc->name), "MACIF:%d",
+                         desc->interface_id);
+}
+
+static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
+                                  u8 *addr, u8 ifidx)
 {
        brcmf_dbg(TRACE,
                  "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx);
        desc->occupied = 1;
        desc->state = BRCMF_FWS_STATE_OPEN;
        desc->requested_credit = 0;
+       desc->requested_packet = 0;
        /* depending on use may need ifp->bssidx instead */
        desc->interface_id = ifidx;
        desc->ac_bitmap = 0xff; /* update this when handling APSD */
@@ -660,22 +648,22 @@ static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
 }
 
 static
-void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc)
+void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc)
 {
        brcmf_dbg(TRACE,
                  "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
        desc->occupied = 0;
        desc->state = BRCMF_FWS_STATE_CLOSE;
        desc->requested_credit = 0;
+       desc->requested_packet = 0;
 }
 
 static struct brcmf_fws_mac_descriptor *
-brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
+brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea)
 {
        struct brcmf_fws_mac_descriptor *entry;
        int i;
 
-       brcmf_dbg(TRACE, "enter: ea=%pM\n", ea);
        if (ea == NULL)
                return ERR_PTR(-EINVAL);
 
@@ -690,42 +678,33 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
 }
 
 static struct brcmf_fws_mac_descriptor*
-brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp,
-                       u8 *da)
+brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da)
 {
        struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
        bool multicast;
-       enum nl80211_iftype iftype;
-
-       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
 
        multicast = is_multicast_ether_addr(da);
-       iftype = brcmf_cfg80211_get_iftype(ifp);
 
-       /* Multicast destination and P2P clients get the interface entry.
-        * STA gets the interface entry if there is no exact match. For
-        * example, TDLS destinations have their own entry.
+       /* Multicast destination, STA and P2P clients get the interface entry.
+        * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
+        * have their own entry.
         */
-       entry = NULL;
-       if ((multicast || iftype == NL80211_IFTYPE_STATION ||
-            iftype == NL80211_IFTYPE_P2P_CLIENT) && ifp->fws_desc)
+       if (multicast && ifp->fws_desc) {
                entry = ifp->fws_desc;
-
-       if (entry != NULL && iftype != NL80211_IFTYPE_STATION)
                goto done;
+       }
 
-       entry = brcmf_fws_mac_descriptor_lookup(fws, da);
+       entry = brcmf_fws_macdesc_lookup(fws, da);
        if (IS_ERR(entry))
-               entry = &fws->desc.other;
+               entry = ifp->fws_desc;
 
 done:
-       brcmf_dbg(TRACE, "exit: entry=%p\n", entry);
        return entry;
 }
 
-static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
-                                     struct brcmf_fws_mac_descriptor *entry,
-                                     int fifo)
+static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws,
+                                    struct brcmf_fws_mac_descriptor *entry,
+                                    int fifo)
 {
        struct brcmf_fws_mac_descriptor *if_entry;
        bool closed;
@@ -748,15 +727,11 @@ static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
        return closed || !(entry->ac_bitmap & BIT(fifo));
 }
 
-static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws,
-                                      struct brcmf_fws_mac_descriptor *entry,
-                                      int ifidx)
+static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws,
+                                     struct brcmf_fws_mac_descriptor *entry,
+                                     int ifidx)
 {
-       brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n",
-                 entry->ea, entry->interface_id, ifidx);
        if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) {
-               brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n",
-                         ifidx, entry->psq.len);
                brcmf_fws_psq_flush(fws, &entry->psq, ifidx);
                entry->occupied = !!(entry->psq.len);
        }
@@ -772,7 +747,6 @@ static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws,
        int prec;
        u32 hslot;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        txq = brcmf_bus_gettxq(fws->drvr->bus_if);
        if (IS_ERR(txq)) {
                brcmf_dbg(TRACE, "no txq to clean up\n");
@@ -798,7 +772,6 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        struct brcmf_fws_mac_descriptor *table;
        bool (*matchfn)(struct sk_buff *, void *) = NULL;
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        if (fws == NULL)
                return;
 
@@ -808,51 +781,122 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        /* cleanup individual nodes */
        table = &fws->desc.nodes[0];
        for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++)
-               brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx);
+               brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx);
 
-       brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx);
+       brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx);
        brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx);
        brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
 }
 
-static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx,
-                                struct brcmf_fws_mac_descriptor *entry,
-                                int prec)
+static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
-       brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea);
-       if (entry->state == BRCMF_FWS_STATE_CLOSE) {
-               /* check delayedQ and suppressQ in one call using bitmap */
-               if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0)
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp & ~NBITVAL(prec);
-               else
-                       entry->traffic_pending_bmp =
-                               entry->traffic_pending_bmp | NBITVAL(prec);
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+       u8 *wlh;
+       u16 data_offset = 0;
+       u8 fillers;
+       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
+
+       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u (%u), pkttag=0x%08X, hslot=%d\n",
+                 entry->ea, entry->interface_id,
+                 brcmf_skb_if_flags_get_field(skb, INDEX),
+                 le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff);
+       if (entry->send_tim_signal)
+               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+
+       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
+       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
+       fillers = round_up(data_offset, 4) - data_offset;
+       data_offset += fillers;
+
+       skb_push(skb, data_offset);
+       wlh = skb->data;
+
+       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
+       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
+       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
+       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
+
+       if (entry->send_tim_signal) {
+               entry->send_tim_signal = 0;
+               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
+               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
+               wlh[2] = entry->mac_handle;
+               wlh[3] = entry->traffic_pending_bmp;
+               brcmf_dbg(TRACE, "adding TIM info: %02X:%02X:%02X:%02X\n",
+                         wlh[0], wlh[1], wlh[2], wlh[3]);
+               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
+               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
        }
-       /* request a TIM update to firmware at the next piggyback opportunity */
+       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;
+}
+
+static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
+                                struct brcmf_fws_mac_descriptor *entry,
+                                int fifo, bool send_immediately)
+{
+       struct sk_buff *skb;
+       struct brcmf_bus *bus;
+       struct brcmf_skbuff_cb *skcb;
+       s32 err;
+       u32 len;
+
+       /* check delayedQ and suppressQ in one call using bitmap */
+       if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
+               entry->traffic_pending_bmp &= ~NBITVAL(fifo);
+       else
+               entry->traffic_pending_bmp |= NBITVAL(fifo);
+
+       entry->send_tim_signal = false;
        if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp)
                entry->send_tim_signal = true;
+       if (send_immediately && entry->send_tim_signal &&
+           entry->state == BRCMF_FWS_STATE_CLOSE) {
+               /* create a dummy packet and sent that. The traffic          */
+               /* bitmap info will automatically be attached to that packet */
+               len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 +
+                     BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 +
+                     4 + fws->drvr->hdrlen;
+               skb = brcmu_pkt_buf_get_skb(len);
+               if (skb == NULL)
+                       return false;
+               skb_pull(skb, len);
+               skcb = brcmf_skbcb(skb);
+               skcb->mac = entry;
+               skcb->state = BRCMF_FWS_SKBSTATE_TIM;
+               bus = fws->drvr->bus_if;
+               err = brcmf_fws_hdrpush(fws, skb);
+               if (err == 0)
+                       err = brcmf_bus_txdata(bus, skb);
+               if (err)
+                       brcmu_pkt_buf_free_skb(skb);
+               return true;
+       }
+       return false;
 }
 
 static void
 brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
                             u8 if_id)
 {
-       struct brcmf_if *ifp = fws->drvr->iflist[if_id];
+       struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
 
        if (WARN_ON(!ifp))
                return;
 
-       brcmf_dbg(TRACE,
-                 "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx);
-
        if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
            pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
                brcmf_txflowblock_if(ifp,
                                     BRCMF_NETIF_STOP_REASON_FWS_FC, false);
        if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
-           pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER)
+           pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) {
+               fws->stats.fws_flow_block++;
                brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true);
+       }
        return;
 }
 
@@ -876,34 +920,38 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
 
        entry = &fws->desc.nodes[mac_handle & 0x1F];
        if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
-               brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx);
                if (entry->occupied) {
-                       brcmf_fws_mac_desc_cleanup(fws, entry, -1);
-                       brcmf_fws_clear_mac_descriptor(entry);
+                       brcmf_dbg(TRACE, "deleting %s mac %pM\n",
+                                 entry->name, addr);
+                       brcmf_fws_macdesc_cleanup(fws, entry, -1);
+                       brcmf_fws_macdesc_deinit(entry);
                } else
                        fws->stats.mac_update_failed++;
                return 0;
        }
 
-       brcmf_dbg(TRACE,
-                 "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx);
-       existing = brcmf_fws_mac_descriptor_lookup(fws, addr);
+       existing = brcmf_fws_macdesc_lookup(fws, addr);
        if (IS_ERR(existing)) {
                if (!entry->occupied) {
                        entry->mac_handle = mac_handle;
-                       brcmf_fws_init_mac_descriptor(entry, addr, ifidx);
+                       brcmf_fws_macdesc_init(entry, addr, ifidx);
+                       brcmf_fws_macdesc_set_name(fws, entry);
                        brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
                                        BRCMF_FWS_PSQ_LEN);
+                       brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
                } else {
                        fws->stats.mac_update_failed++;
                }
        } else {
                if (entry != existing) {
-                       brcmf_dbg(TRACE, "relocate mac\n");
+                       brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
                        memcpy(entry, existing,
                               offsetof(struct brcmf_fws_mac_descriptor, psq));
                        entry->mac_handle = mac_handle;
-                       brcmf_fws_clear_mac_descriptor(existing);
+                       brcmf_fws_macdesc_deinit(existing);
+                       brcmf_fws_macdesc_set_name(fws, entry);
+                       brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
+                                 addr);
                } else {
                        brcmf_dbg(TRACE, "use existing\n");
                        WARN_ON(entry->mac_handle != mac_handle);
@@ -918,7 +966,6 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
 {
        struct brcmf_fws_mac_descriptor *entry;
        u8 mac_handle;
-       int i;
 
        mac_handle = data[0];
        entry = &fws->desc.nodes[mac_handle & 0x1F];
@@ -926,16 +973,18 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
                fws->stats.mac_ps_update_failed++;
                return -ESRCH;
        }
-
-       /* a state update should wipe old credits? */
+       /* a state update should wipe old credits */
        entry->requested_credit = 0;
+       entry->requested_packet = 0;
        if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
                entry->state = BRCMF_FWS_STATE_OPEN;
                return BRCMF_FWS_RET_OK_SCHEDULE;
        } else {
                entry->state = BRCMF_FWS_STATE_CLOSE;
-               for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++)
-                       brcmf_fws_tim_update(fws, entry, i);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false);
+               brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
        }
        return BRCMF_FWS_RET_OK_NOSCHEDULE;
 }
@@ -949,7 +998,6 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
 
        ifidx = data[0];
 
-       brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
        if (ifidx >= BRCMF_MAX_IFS) {
                ret = -ERANGE;
                goto fail;
@@ -961,6 +1009,8 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
                goto fail;
        }
 
+       brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
+                 entry->name);
        switch (type) {
        case BRCMF_FWS_TYPE_INTERFACE_OPEN:
                entry->state = BRCMF_FWS_STATE_OPEN;
@@ -991,6 +1041,9 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
                return -ESRCH;
        }
 
+       brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
+                 brcmf_fws_get_tlv_name(type), type, entry->name,
+                 data[0], data[2]);
        if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
                entry->requested_credit = data[0];
        else
@@ -1000,6 +1053,37 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
 
+static void
+brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry,
+                                struct sk_buff *skb)
+{
+       if (entry->requested_credit > 0) {
+               entry->requested_credit--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1);
+               if (entry->state != BRCMF_FWS_STATE_CLOSE)
+                       brcmf_err("requested credit set while mac not closed!\n");
+       } else if (entry->requested_packet > 0) {
+               entry->requested_packet--;
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+               if (entry->state != BRCMF_FWS_STATE_CLOSE)
+                       brcmf_err("requested packet set while mac not closed!\n");
+       } else {
+               brcmf_skb_if_flags_set_field(skb, REQUESTED, 0);
+               brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0);
+       }
+}
+
+static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb)
+{
+       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+
+       if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) &&
+           (entry->state == BRCMF_FWS_STATE_CLOSE))
+               entry->requested_credit++;
+}
+
 static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
                                     u8 fifo, u8 credits)
 {
@@ -1010,6 +1094,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
        if (!credits)
                return;
 
+       fws->fifo_credit_map |= 1 << fifo;
+
        if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
            (fws->credits_borrowed[0])) {
                for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
@@ -1031,7 +1117,6 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
                }
        }
 
-       fws->fifo_credit_map |= 1 << fifo;
        fws->fifo_credit[fifo] += credits;
 }
 
@@ -1042,27 +1127,6 @@ static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
                queue_work(fws->fws_wq, &fws->fws_dequeue_work);
 }
 
-static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo,
-                                    struct sk_buff *p)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac;
-
-       if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT)
-                       return;
-               brcmf_fws_return_credits(fws, fifo, 1);
-       } else {
-               /*
-                * if this packet did not count against FIFO credit, it
-                * must have taken a requested_credit from the destination
-                * entry (for pspoll etc.)
-                */
-               if (!brcmf_skb_if_flags_get_field(p, REQUESTED))
-                       entry->requested_credit++;
-       }
-       brcmf_fws_schedule_deq(fws);
-}
-
 static int brcmf_fws_enq(struct brcmf_fws_info *fws,
                         enum brcmf_fws_skb_state state, int fifo,
                         struct sk_buff *p)
@@ -1078,7 +1142,7 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
                return -ENOENT;
        }
 
-       brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len);
+       brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p);
        if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
                prec += 1;
                qfull_stat = &fws->stats.supprq_full_error;
@@ -1095,14 +1159,12 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws,
 
        /* update the sk_buff state */
        brcmf_skbcb(p)->state = state;
-       if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED)
-               entry->suppress_count++;
 
        /*
         * A packet has been pushed so update traffic
         * availability bitmap, if applicable
         */
-       brcmf_fws_tim_update(fws, entry, fifo);
+       brcmf_fws_tim_update(fws, entry, fifo, true);
        brcmf_fws_flow_control_check(fws, &entry->psq,
                                     brcmf_skb_if_flags_get_field(p, INDEX));
        return 0;
@@ -1113,7 +1175,6 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        struct brcmf_fws_mac_descriptor *table;
        struct brcmf_fws_mac_descriptor *entry;
        struct sk_buff *p;
-       int use_credit = 1;
        int num_nodes;
        int node_pos;
        int prec_out;
@@ -1127,7 +1188,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        for (i = 0; i < num_nodes; i++) {
                entry = &table[(node_pos + i) % num_nodes];
                if (!entry->occupied ||
-                   brcmf_fws_mac_desc_closed(fws, entry, fifo))
+                   brcmf_fws_macdesc_closed(fws, entry, fifo))
                        continue;
 
                if (entry->suppressed)
@@ -1137,9 +1198,8 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
                if (p == NULL) {
                        if (entry->suppressed) {
-                               if (entry->suppr_transit_count >
-                                   entry->suppress_count)
-                                       return NULL;
+                               if (entry->suppr_transit_count)
+                                       continue;
                                entry->suppressed = false;
                                p = brcmu_pktq_mdeq(&entry->psq,
                                                    1 << (fifo * 2), &prec_out);
@@ -1148,26 +1208,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                if  (p == NULL)
                        continue;
 
-               /* did the packet come from suppress sub-queue? */
-               if (entry->requested_credit > 0) {
-                       entry->requested_credit--;
-                       /*
-                        * if the packet was pulled out while destination is in
-                        * closed state but had a non-zero packets requested,
-                        * then this should not count against the FIFO credit.
-                        * That is due to the fact that the firmware will
-                        * most likely hold onto this packet until a suitable
-                        * time later to push it to the appropriate AC FIFO.
-                        */
-                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                               use_credit = 0;
-               } else if (entry->requested_packet > 0) {
-                       entry->requested_packet--;
-                       brcmf_skb_if_flags_set_field(p, REQUESTED, 1);
-                       if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                               use_credit = 0;
-               }
-               brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit);
+               brcmf_fws_macdesc_use_req_credit(entry, p);
 
                /* move dequeue position to ensure fair round-robin */
                fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes;
@@ -1179,7 +1220,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
                 * A packet has been picked up, update traffic
                 * availability bitmap, if applicable
                 */
-               brcmf_fws_tim_update(fws, entry, fifo);
+               brcmf_fws_tim_update(fws, entry, fifo, false);
 
                /*
                 * decrement total enqueued fifo packets and
@@ -1192,7 +1233,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
        }
        p = NULL;
 done:
-       brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p);
+       brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p);
        return p;
 }
 
@@ -1202,22 +1243,26 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        u32 hslot;
        int ret;
+       u8 ifidx;
 
        hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
 
        /* this packet was suppressed */
-       if (!entry->suppressed || entry->generation != genbit) {
+       if (!entry->suppressed) {
                entry->suppressed = true;
-               entry->suppress_count = brcmu_pktq_mlen(&entry->psq,
-                                                       1 << (fifo * 2 + 1));
                entry->suppr_transit_count = entry->transit_count;
+               brcmf_dbg(DATA, "suppress %s: transit %d\n",
+                         entry->name, entry->transit_count);
        }
 
        entry->generation = genbit;
 
-       ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
+       ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
+       if (ret == 0)
+               ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
+                                   skb);
        if (ret != 0) {
-               /* suppress q is full, drop this packet */
+               /* suppress q is full or hdrpull failed, drop this packet */
                brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
                                        true);
        } else {
@@ -1225,26 +1270,24 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
                 * Mark suppressed to avoid a double free during
                 * wlfc cleanup
                 */
-               brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot,
-                                                genbit);
-               entry->suppress_count++;
+               brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
        }
 
        return ret;
 }
 
 static int
-brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
+brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                           u32 genbit)
 {
        u32 fifo;
        int ret;
        bool remove_from_hanger = true;
        struct sk_buff *skb;
+       struct brcmf_skbuff_cb *skcb;
        struct brcmf_fws_mac_descriptor *entry = NULL;
 
-       brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n",
-                 flags, hslot);
+       brcmf_dbg(DATA, "flags %d\n", flags);
 
        if (flags == BRCMF_FWS_TXSTATUS_DISCARD)
                fws->stats.txs_discard++;
@@ -1256,6 +1299,8 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                remove_from_hanger = false;
        } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED)
                fws->stats.txs_tossed++;
+       else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)
+               fws->stats.txs_host_tossed++;
        else
                brcmf_err("unexpected txstatus\n");
 
@@ -1266,26 +1311,35 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
                return ret;
        }
 
-       entry = brcmf_skbcb(skb)->mac;
+       skcb = brcmf_skbcb(skb);
+       entry = skcb->mac;
        if (WARN_ON(!entry)) {
                brcmu_pkt_buf_free_skb(skb);
                return -EINVAL;
        }
+       entry->transit_count--;
+       if (entry->suppressed && entry->suppr_transit_count)
+               entry->suppr_transit_count--;
+
+       brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags,
+                 skcb->htod);
 
        /* pick up the implicit credit from this packet */
        fifo = brcmf_skb_htod_tag_get_field(skb, FIFO);
-       brcmf_skb_pick_up_credit(fws, fifo, skb);
+       if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) ||
+           (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) ||
+           (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) {
+               brcmf_fws_return_credits(fws, fifo, 1);
+               brcmf_fws_schedule_deq(fws);
+       }
+       brcmf_fws_macdesc_return_req_credit(skb);
 
        if (!remove_from_hanger)
                ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit);
 
-       if (remove_from_hanger || ret) {
-               entry->transit_count--;
-               if (entry->suppressed)
-                       entry->suppr_transit_count--;
-
+       if (remove_from_hanger || ret)
                brcmf_txfinalize(fws->drvr, skb, true);
-       }
+
        return 0;
 }
 
@@ -1299,11 +1353,11 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
                return BRCMF_FWS_RET_OK_NOSCHEDULE;
        }
 
-       brcmf_dbg(TRACE, "enter: data %pM\n", data);
+       brcmf_dbg(DATA, "enter: data %pM\n", data);
        for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
                brcmf_fws_return_credits(fws, i, data[i]);
 
-       brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map,
+       brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
                  fws->fifo_delay_map);
        return BRCMF_FWS_RET_OK_SCHEDULE;
 }
@@ -1323,7 +1377,7 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
        hslot = brcmf_txstatus_get_field(status, HSLOT);
        genbit = brcmf_txstatus_get_field(status, GENERATION);
 
-       return brcmf_fws_txstatus_process(fws, flags, hslot, genbit);
+       return brcmf_fws_txs_process(fws, flags, hslot, genbit);
 }
 
 static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
@@ -1331,7 +1385,7 @@ static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
        __le32 timestamp;
 
        memcpy(&timestamp, &data[2], sizeof(timestamp));
-       brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1],
+       brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1],
                  le32_to_cpu(timestamp));
        return 0;
 }
@@ -1364,6 +1418,10 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
                brcmf_err("event payload too small (%d)\n", e->datalen);
                return -EINVAL;
        }
+       if (fws->creditmap_received)
+               return 0;
+
+       fws->creditmap_received = true;
 
        brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
        brcmf_fws_lock(ifp->drvr, flags);
@@ -1379,6 +1437,20 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
        return 0;
 }
 
+static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
+                                               const struct brcmf_event_msg *e,
+                                               void *data)
+{
+       struct brcmf_fws_info *fws = ifp->drvr->fws;
+       ulong flags;
+
+       brcmf_fws_lock(ifp->drvr, flags);
+       if (fws)
+               fws->bcmc_credit_check = true;
+       brcmf_fws_unlock(ifp->drvr, flags);
+       return 0;
+}
+
 int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                      struct sk_buff *skb)
 {
@@ -1392,7 +1464,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        s32 status;
        s32 err;
 
-       brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
+       brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
                  ifidx, skb->len, signal_len);
 
        WARN_ON(signal_len > skb->len);
@@ -1426,14 +1498,15 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
                len = signal_data[1];
                data = signal_data + 2;
 
-               brcmf_dbg(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type,
-                         brcmf_fws_get_tlv_name(type), len, *data);
+               brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n",
+                         brcmf_fws_get_tlv_name(type), type, len,
+                         brcmf_fws_get_tlv_len(fws, type));
 
                /* abort parsing when length invalid */
                if (data_len < len + 2)
                        break;
 
-               if (len != brcmf_fws_get_tlv_len(fws, type))
+               if (len < brcmf_fws_get_tlv_len(fws, type))
                        break;
 
                err = BRCMF_FWS_RET_OK_NOSCHEDULE;
@@ -1502,64 +1575,32 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        return 0;
 }
 
-static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
-{
-       struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
-       u8 *wlh;
-       u16 data_offset = 0;
-       u8 fillers;
-       __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod);
-
-       brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n",
-                 entry->ea, entry->interface_id, le32_to_cpu(pkttag));
-       if (entry->send_tim_signal)
-               data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-
-       /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
-       data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN;
-       fillers = round_up(data_offset, 4) - data_offset;
-       data_offset += fillers;
-
-       skb_push(skb, data_offset);
-       wlh = skb->data;
-
-       wlh[0] = BRCMF_FWS_TYPE_PKTTAG;
-       wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN;
-       memcpy(&wlh[2], &pkttag, sizeof(pkttag));
-       wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2;
-
-       if (entry->send_tim_signal) {
-               entry->send_tim_signal = 0;
-               wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP;
-               wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN;
-               wlh[2] = entry->mac_handle;
-               wlh[3] = entry->traffic_pending_bmp;
-               wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2;
-               entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
-       }
-       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;
-}
-
 static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                                   struct sk_buff *p)
 {
        struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
        struct brcmf_fws_mac_descriptor *entry = skcb->mac;
        int rc = 0;
-       bool header_needed;
+       bool first_time;
        int hslot = BRCMF_FWS_HANGER_MAXITEMS;
        u8 free_ctr;
-       u8 ifidx;
        u8 flags;
 
-       header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED;
+       first_time = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED;
 
-       if (header_needed) {
+       brcmf_skb_if_flags_set_field(p, TRANSMIT, 1);
+       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
+       brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation);
+       flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
+       if (brcmf_skb_if_flags_get_field(p, REQUESTED)) {
+               /*
+                * Indicate that this packet is being sent in response to an
+                * explicit request from the firmware side.
+                */
+               flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
+       }
+       brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
+       if (first_time) {
                /* obtaining free slot may fail, but that will be caught
                 * by the hanger push. This assures the packet has a BDC
                 * header upon return.
@@ -1568,47 +1609,20 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                free_ctr = entry->seq[fifo];
                brcmf_skb_htod_tag_set_field(p, HSLOT, hslot);
                brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr);
-               brcmf_skb_htod_tag_set_field(p, GENERATION, 1);
-               entry->transit_count++;
-       }
-       brcmf_skb_if_flags_set_field(p, TRANSMIT, 1);
-       brcmf_skb_htod_tag_set_field(p, FIFO, fifo);
-
-       flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST;
-       if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) {
-               /*
-               Indicate that this packet is being sent in response to an
-               explicit request from the firmware side.
-               */
-               flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
-       }
-       brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
-       if (header_needed) {
-               brcmf_fws_hdrpush(fws, p);
                rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot);
                if (rc)
                        brcmf_err("hanger push failed: rc=%d\n", rc);
-       } else {
-               int gen;
-
-               /* remove old header */
-               rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p);
-               if (rc == 0) {
-                       hslot = brcmf_skb_htod_tag_get_field(p, HSLOT);
-                       brcmf_fws_hanger_get_genbit(&fws->hanger, p,
-                                                   hslot, &gen);
-                       brcmf_skb_htod_tag_set_field(p, GENERATION, gen);
-
-                       /* push new header */
-                       brcmf_fws_hdrpush(fws, p);
-               }
        }
 
+       if (rc == 0)
+               brcmf_fws_hdrpush(fws, p);
+
        return rc;
 }
 
 static void
-brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
+brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
+                      struct sk_buff *skb, int fifo)
 {
        /*
        put the packet back to the head of queue
@@ -1622,13 +1636,11 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
        enum brcmf_fws_skb_state state;
        struct sk_buff *pktout;
        int rc = 0;
-       int fifo;
        int hslot;
-       u8 ifidx;
 
-       fifo = brcmf_skb_if_flags_get_field(skb, FIFO);
        state = brcmf_skbcb(skb)->state;
        entry = brcmf_skbcb(skb)->mac;
+       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
 
        if (entry != NULL) {
                if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) {
@@ -1640,19 +1652,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
                                rc = -ENOSPC;
                        }
                } else {
-                       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
-
-                       /* remove header first */
-                       rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-                       if (rc) {
-                               brcmf_err("header removal failed\n");
-                               /* free the hanger slot */
-                               brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
-                                                       &pktout, true);
-                               rc = -EINVAL;
-                               goto fail;
-                       }
-
                        /* delay-q packets are going to delay-q */
                        pktout = brcmu_pktq_penq_head(&entry->psq,
                                                      2 * fifo, skb);
@@ -1668,33 +1667,30 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
                        /* decrement sequence count */
                        entry->seq[fifo]--;
                }
-               /*
-               if this packet did not count against FIFO credit, it must have
-               taken a requested_credit from the firmware (for pspoll etc.)
-               */
-               if (!(brcmf_skbcb(skb)->if_flags &
-                     BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK))
-                       entry->requested_credit++;
        } else {
                brcmf_err("no mac entry linked\n");
                rc = -ENOENT;
        }
 
-
-fail:
        if (rc) {
-               brcmf_txfinalize(fws->drvr, skb, false);
                fws->stats.rollback_failed++;
-       } else
+               brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED,
+                                     hslot, 0);
+       } else {
                fws->stats.rollback_success++;
+               brcmf_fws_return_credits(fws, fifo, 1);
+               brcmf_fws_macdesc_return_req_credit(skb);
+       }
 }
 
 static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
 {
        int lender_ac;
 
-       if (time_after(fws->borrow_defer_timestamp, jiffies))
+       if (time_after(fws->borrow_defer_timestamp, jiffies)) {
+               fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
                return -ENAVAIL;
+       }
 
        for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
                if (fws->fifo_credit[lender_ac]) {
@@ -1702,10 +1698,12 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
                        fws->fifo_credit[lender_ac]--;
                        if (fws->fifo_credit[lender_ac] == 0)
                                fws->fifo_credit_map &= ~(1 << lender_ac);
-                       brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac);
+                       fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE);
+                       brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
                        return 0;
                }
        }
+       fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
        return -ENAVAIL;
 }
 
@@ -1714,33 +1712,6 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
 {
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        int *credit = &fws->fifo_credit[fifo];
-       int use_credit = 1;
-
-       brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit);
-
-       if (entry->requested_credit > 0) {
-               /*
-                * if the packet was pulled out while destination is in
-                * closed state but had a non-zero packets requested,
-                * then this should not count against the FIFO credit.
-                * That is due to the fact that the firmware will
-                * most likely hold onto this packet until a suitable
-                * time later to push it to the appropriate AC FIFO.
-                */
-               entry->requested_credit--;
-               if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                       use_credit = 0;
-       } else if (entry->requested_packet > 0) {
-               entry->requested_packet--;
-               brcmf_skb_if_flags_set_field(skb, REQUESTED, 1);
-               if (entry->state == BRCMF_FWS_STATE_CLOSE)
-                       use_credit = 0;
-       }
-       brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit);
-       if (!use_credit) {
-               brcmf_dbg(TRACE, "exit: no creditcheck set\n");
-               return 0;
-       }
 
        if (fifo != BRCMF_FWS_FIFO_AC_BE)
                fws->borrow_defer_timestamp = jiffies +
@@ -1748,17 +1719,22 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
 
        if (!(*credit)) {
                /* Try to borrow a credit from other queue */
-               if (fifo == BRCMF_FWS_FIFO_AC_BE &&
-                   brcmf_fws_borrow_credit(fws) == 0)
-                       return 0;
-
-               brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo);
-               return -ENAVAIL;
+               if (fifo != BRCMF_FWS_FIFO_AC_BE ||
+                   (brcmf_fws_borrow_credit(fws) != 0)) {
+                       brcmf_dbg(DATA, "ac=%d, credits depleted\n", fifo);
+                       return -ENAVAIL;
+               }
+       } else {
+               (*credit)--;
+               if (!(*credit))
+                       fws->fifo_credit_map &= ~(1 << fifo);
        }
-       (*credit)--;
-       if (!(*credit))
-               fws->fifo_credit_map &= ~(1 << fifo);
-       brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit);
+
+       brcmf_fws_macdesc_use_req_credit(entry, skb);
+
+       brcmf_dbg(DATA, "ac=%d, credits=%02d:%02d:%02d:%02d\n", fifo,
+                 fws->fifo_credit[0], fws->fifo_credit[1],
+                 fws->fifo_credit[2], fws->fifo_credit[3]);
        return 0;
 }
 
@@ -1769,6 +1745,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
        struct brcmf_fws_mac_descriptor *entry;
        struct brcmf_bus *bus = fws->drvr->bus_if;
        int rc;
+       u8 ifidx;
 
        entry = skcb->mac;
        if (IS_ERR(entry))
@@ -1780,21 +1757,27 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
                goto rollback;
        }
 
+       brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags,
+                 skcb->htod);
        rc = brcmf_bus_txdata(bus, skb);
-       if (rc < 0)
+       if (rc < 0) {
+               brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
                goto rollback;
+       }
 
+       entry->transit_count++;
+       if (entry->suppressed)
+               entry->suppr_transit_count++;
        entry->seq[fifo]++;
        fws->stats.pkt2bus++;
-       if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               fws->stats.send_pkts[fifo]++;
-               fws->stats.fifo_credits_sent[fifo]++;
-       }
+       fws->stats.send_pkts[fifo]++;
+       if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
+               fws->stats.requested_sent[fifo]++;
 
        return rc;
 
 rollback:
-       brcmf_fws_rollback_toq(fws, skb);
+       brcmf_fws_rollback_toq(fws, skb, fifo);
        return rc;
 }
 
@@ -1826,19 +1809,25 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
 
        /* set control buffer information */
        skcb->if_flags = 0;
-       skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest);
+       skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest);
        skcb->state = BRCMF_FWS_SKBSTATE_NEW;
        brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
        if (!multicast)
                fifo = brcmf_fws_prio2fifo[skb->priority];
-       brcmf_skb_if_flags_set_field(skb, FIFO, fifo);
 
-       brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest,
-                 multicast, fifo);
+       brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name,
+                 eh->h_dest, multicast, fifo);
 
        brcmf_fws_lock(drvr, flags);
+       /* multicast credit support is conditional, setting
+        * flag to false to assure credit is consumed below.
+        */
+       if (fws->bcmc_credit_check)
+               multicast = false;
+
        if (skcb->mac->suppressed ||
-           brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) ||
+           fws->bus_flow_blocked ||
+           brcmf_fws_macdesc_closed(fws, skcb->mac, fifo) ||
            brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) ||
            (!multicast &&
             brcmf_fws_consume_credit(fws, fifo, skb) < 0)) {
@@ -1846,9 +1835,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                drvr->fws->fifo_delay_map |= 1 << fifo;
                brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
        } else {
-               if (brcmf_fws_commit_skb(fws, fifo, skb))
-                       if (!multicast)
-                               brcmf_skb_pick_up_credit(fws, fifo, skb);
+               brcmf_fws_commit_skb(fws, fifo, skb);
        }
        brcmf_fws_unlock(drvr, flags);
        return 0;
@@ -1862,7 +1849,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp)
        if (!entry)
                return;
 
-       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
 }
 
 void brcmf_fws_add_interface(struct brcmf_if *ifp)
@@ -1870,16 +1857,16 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
        struct brcmf_fws_info *fws = ifp->drvr->fws;
        struct brcmf_fws_mac_descriptor *entry;
 
-       brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n",
-                 ifp->bssidx, ifp->mac_addr);
        if (!ifp->ndev || !ifp->drvr->fw_signals)
                return;
 
        entry = &fws->desc.iface[ifp->ifidx];
        ifp->fws_desc = entry;
-       brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx);
+       brcmf_fws_macdesc_set_name(fws, entry);
        brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
                        BRCMF_FWS_PSQ_LEN);
+       brcmf_dbg(TRACE, "added %s\n", entry->name);
 }
 
 void brcmf_fws_del_interface(struct brcmf_if *ifp)
@@ -1887,13 +1874,13 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
        struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
        ulong flags;
 
-       brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
        if (!entry)
                return;
 
        brcmf_fws_lock(ifp->drvr, flags);
        ifp->fws_desc = NULL;
-       brcmf_fws_clear_mac_descriptor(entry);
+       brcmf_dbg(TRACE, "deleting %s\n", entry->name);
+       brcmf_fws_macdesc_deinit(entry);
        brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
        brcmf_fws_unlock(ifp->drvr, flags);
 }
@@ -1904,39 +1891,37 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
        struct sk_buff *skb;
        ulong flags;
        int fifo;
-       int credit;
 
        fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
 
-       brcmf_dbg(TRACE, "enter: fws=%p\n", fws);
        brcmf_fws_lock(fws->drvr, flags);
-       for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) {
-               brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo,
-                         fws->fifo_credit[fifo]);
-               for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
+       for (fifo = NL80211_NUM_ACS; fifo >= 0 && !fws->bus_flow_blocked;
+            fifo--) {
+               while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
+                      (fifo == BRCMF_FWS_FIFO_BCMC))) {
                        skb = brcmf_fws_deq(fws, fifo);
-                       if (!skb || brcmf_fws_commit_skb(fws, fifo, skb))
+                       if (!skb)
+                               break;
+                       fws->fifo_credit[fifo]--;
+                       if (brcmf_fws_commit_skb(fws, fifo, skb))
+                               break;
+                       if (fws->bus_flow_blocked)
                                break;
-                       if (brcmf_skbcb(skb)->if_flags &
-                           BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
-                               credit++;
                }
                if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
-                   (credit == fws->fifo_credit[fifo])) {
-                       fws->fifo_credit[fifo] -= credit;
+                   (fws->fifo_credit[fifo] == 0) &&
+                   (!fws->bus_flow_blocked)) {
                        while (brcmf_fws_borrow_credit(fws) == 0) {
                                skb = brcmf_fws_deq(fws, fifo);
                                if (!skb) {
                                        brcmf_fws_return_credits(fws, fifo, 1);
                                        break;
                                }
-                               if (brcmf_fws_commit_skb(fws, fifo, skb)) {
-                                       brcmf_fws_return_credits(fws, fifo, 1);
+                               if (brcmf_fws_commit_skb(fws, fifo, skb))
+                                       break;
+                               if (fws->bus_flow_blocked)
                                        break;
-                               }
                        }
-               } else {
-                       fws->fifo_credit[fifo] -= credit;
                }
        }
        brcmf_fws_unlock(fws->drvr, flags);
@@ -1982,6 +1967,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
                brcmf_err("register credit map handler failed\n");
                goto fail;
        }
+       rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT,
+                                brcmf_fws_notify_bcmc_credit_support);
+       if (rc < 0) {
+               brcmf_err("register bcmc credit handler failed\n");
+               brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
+               goto fail;
+       }
 
        /* setting the iovar may fail if feature is unsupported
         * so leave the rc as is so driver initialization can
@@ -1993,19 +1985,20 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
        }
 
        brcmf_fws_hanger_init(&drvr->fws->hanger);
-       brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0);
+       brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0);
+       brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other);
        brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
                        BRCMF_FWS_PSQ_LEN);
 
        /* create debugfs file for statistics */
        brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
 
-       /* TODO: remove upon feature delivery */
-       brcmf_err("%s bdcv2 tlv signaling [%x]\n",
+       brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
                  drvr->fw_signals ? "enabled" : "disabled", tlv);
        return 0;
 
 fail_event:
+       brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT);
        brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
 fail:
        brcmf_fws_deinit(drvr);
@@ -2043,25 +2036,31 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
        if (!fws)
                return false;
 
-       brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode);
        return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
 }
 
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
        ulong flags;
+       u32 hslot;
 
-       brcmf_fws_lock(fws->drvr, flags);
-       brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED,
-                                  brcmf_skb_htod_tag_get_field(skb, HSLOT), 0);
-       /* the packet never reached firmware so reclaim credit */
-       if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT &&
-           brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) {
-               brcmf_fws_return_credits(fws,
-                                        brcmf_skb_htod_tag_get_field(skb,
-                                                                     FIFO),
-                                        1);
-               brcmf_fws_schedule_deq(fws);
+       if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
+               brcmu_pkt_buf_free_skb(skb);
+               return;
        }
+       brcmf_fws_lock(fws->drvr, flags);
+       hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
+       brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0);
        brcmf_fws_unlock(fws->drvr, flags);
 }
+
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
+{
+       struct brcmf_fws_info *fws = drvr->fws;
+
+       fws->bus_flow_blocked = flow_blocked;
+       if (!flow_blocked)
+               brcmf_fws_schedule_deq(fws);
+       else
+               fws->stats.bus_flow_block++;
+}
index fbe483d..9fc8609 100644 (file)
@@ -29,5 +29,6 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp);
 void brcmf_fws_add_interface(struct brcmf_if *ifp);
 void brcmf_fws_del_interface(struct brcmf_if *ifp);
 void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
+void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
 
 #endif /* FWSIGNAL_H_ */
index 7c1b633..793df66 100644 (file)
@@ -170,7 +170,6 @@ struct brcmf_sdio_dev {
        atomic_t suspend;               /* suspend flag */
        wait_queue_head_t request_byte_wait;
        wait_queue_head_t request_word_wait;
-       wait_queue_head_t request_chain_wait;
        wait_queue_head_t request_buffer_wait;
        struct device *dev;
        struct brcmf_bus *bus_if;
@@ -272,16 +271,6 @@ brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
                         uint rw, uint fnc, uint addr,
                         u32 *word, uint nbyte);
 
-/* read or write any buffer using cmd53 */
-extern int
-brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev,
-                          uint fix_inc, uint rw, uint fnc_num, u32 addr,
-                          struct sk_buff *pkt);
-extern int
-brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc,
-                         uint write, uint func, uint addr,
-                         struct sk_buff_head *pktq);
-
 /* Watchdog timer interface for pm ops */
 extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev,
                                    bool enable);
@@ -291,4 +280,8 @@ extern void brcmf_sdbrcm_disconnect(void *ptr);
 extern void brcmf_sdbrcm_isr(void *arg);
 
 extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+
+extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
+                                wait_queue_head_t *wq);
+extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev);
 #endif                         /* _BRCM_SDH_H_ */
index 9df1f7a..bc29171 100644 (file)
@@ -87,6 +87,27 @@ TRACE_EVENT(brcmf_hexdump,
        TP_printk("hexdump [length=%lu]", __entry->len)
 );
 
+TRACE_EVENT(brcmf_bdchdr,
+       TP_PROTO(void *data),
+       TP_ARGS(data),
+       TP_STRUCT__entry(
+               __field(u8, flags)
+               __field(u8, prio)
+               __field(u8, flags2)
+               __field(u32, siglen)
+               __dynamic_array(u8, signal, *((u8 *)data + 3) * 4)
+       ),
+       TP_fast_assign(
+               __entry->flags = *(u8 *)data;
+               __entry->prio = *((u8 *)data + 1);
+               __entry->flags2 = *((u8 *)data + 2);
+               __entry->siglen = *((u8 *)data + 3) * 4;
+               memcpy(__get_dynamic_array(signal),
+                      (u8 *)data + 4, __entry->siglen);
+       ),
+       TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen)
+);
+
 #ifdef CONFIG_BRCM_TRACING
 
 #undef TRACE_INCLUDE_PATH
index 01aed7a..322cadc 100644 (file)
@@ -82,6 +82,7 @@ struct brcmf_usbdev_info {
        int tx_high_watermark;
        int tx_freecount;
        bool tx_flowblock;
+       spinlock_t tx_flowblock_lock;
 
        struct brcmf_usbreq *tx_reqs;
        struct brcmf_usbreq *rx_reqs;
@@ -411,6 +412,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
 {
        struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context;
        struct brcmf_usbdev_info *devinfo = req->devinfo;
+       unsigned long flags;
 
        brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status,
                  req->skb);
@@ -419,11 +421,13 @@ static void brcmf_usb_tx_complete(struct urb *urb)
        brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0);
        req->skb = NULL;
        brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount);
+       spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
        if (devinfo->tx_freecount > devinfo->tx_high_watermark &&
                devinfo->tx_flowblock) {
                brcmf_txflowblock(devinfo->dev, false);
                devinfo->tx_flowblock = false;
        }
+       spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
 }
 
 static void brcmf_usb_rx_complete(struct urb *urb)
@@ -568,6 +572,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
        struct brcmf_usbreq  *req;
        int ret;
+       unsigned long flags;
 
        brcmf_dbg(USB, "Enter, skb=%p\n", skb);
        if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
@@ -599,11 +604,13 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
                goto fail;
        }
 
+       spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags);
        if (devinfo->tx_freecount < devinfo->tx_low_watermark &&
            !devinfo->tx_flowblock) {
                brcmf_txflowblock(dev, true);
                devinfo->tx_flowblock = true;
        }
+       spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
        return 0;
 
 fail:
@@ -1164,6 +1171,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
 
        /* Initialize the spinlocks */
        spin_lock_init(&devinfo->qlock);
+       spin_lock_init(&devinfo->tx_flowblock_lock);
 
        INIT_LIST_HEAD(&devinfo->rx_freeq);
        INIT_LIST_HEAD(&devinfo->rx_postq);
index 301e572..277b37a 100644 (file)
@@ -3982,6 +3982,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        struct brcmf_fil_af_params_le *af_params;
        bool ack;
        s32 chan_nr;
+       u32 freq;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -3994,6 +3995,8 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -EPERM;
        }
 
+       vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
        if (ieee80211_is_probe_resp(mgmt->frame_control)) {
                /* Right now the only reason to get a probe response */
                /* is for p2p listen response or for p2p GO from     */
@@ -4009,7 +4012,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                ie_offset =  DOT11_MGMT_HDR_LEN +
                             DOT11_BCN_PRB_FIXED_LEN;
                ie_len = len - ie_offset;
-               vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
                if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
                        vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
                err = brcmf_vif_set_mgmt_ie(vif,
@@ -4033,16 +4035,22 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
                /* Add the length exepted for 802.11 header  */
                action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
-               /* Add the channel */
-               chan_nr = ieee80211_frequency_to_channel(chan->center_freq);
+               /* Add the channel. Use the one specified as parameter if any or
+                * the current one (got from the firmware) otherwise
+                */
+               if (chan)
+                       freq = chan->center_freq;
+               else
+                       brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
+                                             &freq);
+               chan_nr = ieee80211_frequency_to_channel(freq);
                af_params->channel = cpu_to_le32(chan_nr);
 
                memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
                       le16_to_cpu(action_frame->len));
 
                brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
-                         *cookie, le16_to_cpu(action_frame->len),
-                         chan->center_freq);
+                         *cookie, le16_to_cpu(action_frame->len), freq);
 
                ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
                                                  af_params);
index 9f9adb4..da88503 100644 (file)
@@ -245,6 +245,14 @@ module_param(cw1200_ba_tx_tids, int, 0644);
 MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
 MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support cw1200_wowlan_support = {
+       /* Support only for limited wowlan functionalities */
+       .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+
+
 static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
                                                const bool have_5ghz)
 {
@@ -289,10 +297,7 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
                                          BIT(NL80211_IFTYPE_P2P_GO);
 
 #ifdef CONFIG_PM
-       /* Support only for limited wowlan functionalities */
-       hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
-               WIPHY_WOWLAN_DISCONNECT;
-       hw->wiphy->wowlan.n_patterns = 0;
+       hw->wiphy->wowlan = &cw1200_wowlan_support;
 #endif
 
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
index dce5e8f..9581d07 100644 (file)
@@ -3727,7 +3727,8 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         * 5. Setup HW Constants
         * ********************/
        /* Device-specific setup */
-       if (il3945_hw_set_hw_params(il)) {
+       err = il3945_hw_set_hw_params(il);
+       if (err) {
                IL_ERR("failed to set hw settings\n");
                goto out_eeprom_free;
        }
index dc1e6da..c092033 100644 (file)
@@ -331,6 +331,19 @@ il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
                return;
        }
 
+       /*
+        * Firmware will not transmit frame on passive channel, if it not yet
+        * received some valid frame on that channel. When this error happen
+        * we have to wait until firmware will unblock itself i.e. when we
+        * note received beacon or other frame. We unblock queues in
+        * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
+        */
+       if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
+           il->iw_mode == NL80211_IFTYPE_STATION) {
+               il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Stopped queues - RX waiting on passive channel\n");
+       }
+
        txq->time_stamp = jiffies;
        info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]);
        ieee80211_tx_info_clear_status(info);
@@ -488,6 +501,11 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
                return;
        }
 
+       if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
+               il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Woke queues - frame received on passive channel\n");
+       }
+
        skb = dev_alloc_skb(128);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
index 3c4899b..b9b2bb5 100644 (file)
@@ -588,6 +588,11 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
                return;
        }
 
+       if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) {
+               il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Woke queues - frame received on passive channel\n");
+       }
+
        /* In case of HW accelerated crypto and bad decryption, drop */
        if (!il->cfg->mod_params->sw_crypto &&
            il_set_decrypted_flag(il, hdr, ampdu_status, stats))
@@ -2806,6 +2811,19 @@ il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb)
                return;
        }
 
+       /*
+        * Firmware will not transmit frame on passive channel, if it not yet
+        * received some valid frame on that channel. When this error happen
+        * we have to wait until firmware will unblock itself i.e. when we
+        * note received beacon or other frame. We unblock queues in
+        * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed.
+        */
+       if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) &&
+           il->iw_mode == NL80211_IFTYPE_STATION) {
+               il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+               D_INFO("Stopped queues - RX waiting on passive channel\n");
+       }
+
        spin_lock_irqsave(&il->sta_lock, flags);
        if (txq->sched_retry) {
                const u32 scd_ssn = il4965_get_scd_ssn(tx_resp);
@@ -5741,7 +5759,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
        hw->flags =
            IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION |
            IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT |
-           IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+           IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS |
+           IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
        if (il->cfg->sku & IL_SKU_N)
                hw->flags |=
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
index e9a3cbc..3195aad 100644 (file)
@@ -5306,6 +5306,17 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        if (changes & BSS_CHANGED_BSSID) {
                D_MAC80211("BSSID %pM\n", bss_conf->bssid);
 
+               /*
+                * On passive channel we wait with blocked queues to see if
+                * there is traffic on that channel. If no frame will be
+                * received (what is very unlikely since scan detects AP on
+                * that channel, but theoretically possible), mac80211 associate
+                * procedure will time out and mac80211 will call us with NULL
+                * bssid. We have to unblock queues on such condition.
+                */
+               if (is_zero_ether_addr(bss_conf->bssid))
+                       il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE);
+
                /*
                 * If there is currently a HW scan going on in the background,
                 * then we need to cancel it, otherwise sometimes we are not
index 4caaf52..83f8ed8 100644 (file)
@@ -1299,6 +1299,8 @@ struct il_priv {
        /* queue refcounts */
 #define IL_MAX_HW_QUEUES       32
        unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)];
+#define IL_STOP_REASON_PASSIVE 0
+       unsigned long stop_reason;
        /* for each AC */
        atomic_t queue_stop_count[4];
 
@@ -2256,6 +2258,19 @@ il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq)
        txq->swq_id = (hwq << 2) | ac;
 }
 
+static inline void
+_il_wake_queue(struct il_priv *il, u8 ac)
+{
+       if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0)
+               ieee80211_wake_queue(il->hw, ac);
+}
+
+static inline void
+_il_stop_queue(struct il_priv *il, u8 ac)
+{
+       if (atomic_inc_return(&il->queue_stop_count[ac]) > 0)
+               ieee80211_stop_queue(il->hw, ac);
+}
 static inline void
 il_wake_queue(struct il_priv *il, struct il_tx_queue *txq)
 {
@@ -2264,8 +2279,7 @@ il_wake_queue(struct il_priv *il, struct il_tx_queue *txq)
        u8 hwq = (queue >> 2) & 0x1f;
 
        if (test_and_clear_bit(hwq, il->queue_stopped))
-               if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0)
-                       ieee80211_wake_queue(il->hw, ac);
+               _il_wake_queue(il, ac);
 }
 
 static inline void
@@ -2276,8 +2290,27 @@ il_stop_queue(struct il_priv *il, struct il_tx_queue *txq)
        u8 hwq = (queue >> 2) & 0x1f;
 
        if (!test_and_set_bit(hwq, il->queue_stopped))
-               if (atomic_inc_return(&il->queue_stop_count[ac]) > 0)
-                       ieee80211_stop_queue(il->hw, ac);
+               _il_stop_queue(il, ac);
+}
+
+static inline void
+il_wake_queues_by_reason(struct il_priv *il, int reason)
+{
+       u8 ac;
+
+       if (test_and_clear_bit(reason, &il->stop_reason))
+               for (ac = 0; ac < 4; ac++)
+                       _il_wake_queue(il, ac);
+}
+
+static inline void
+il_stop_queues_by_reason(struct il_priv *il, int reason)
+{
+       u8 ac;
+
+       if (!test_and_set_bit(reason, &il->stop_reason))
+               for (ac = 0; ac < 4; ac++)
+                       _il_stop_queue(il, ac);
 }
 
 #ifdef ieee80211_stop_queue
index 3b5613e..f55a758 100644 (file)
@@ -7,14 +7,16 @@ iwlwifi-objs          += iwl-notif-wait.o
 iwlwifi-objs           += iwl-eeprom-read.o iwl-eeprom-parse.o
 iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
-iwlwifi-objs           += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwl-7000.o
+iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o
+
+iwlwifi-objs += $(iwlwifi-m)
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
 
-
 obj-$(CONFIG_IWLDVM)   += dvm/
 obj-$(CONFIG_IWLMVM)   += mvm/
 
index f1b8df1..5cd87f9 100644 (file)
@@ -915,6 +915,9 @@ struct iwl_priv {
        __le64 replay_ctr;
        __le16 last_seq_ctl;
        bool have_rekey_data;
+#ifdef CONFIG_PM_SLEEP
+       struct wiphy_wowlan_support wowlan_support;
+#endif
 
        /* device_pointers: pointers to ucode event tables */
        struct {
index c0039a9..eef64bb 100644 (file)
@@ -208,20 +208,21 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
            priv->trans->ops->d3_suspend &&
            priv->trans->ops->d3_resume &&
            device_can_wakeup(priv->trans->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT |
-                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                            WIPHY_WOWLAN_DISCONNECT |
+                                            WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                            WIPHY_WOWLAN_RFKILL_RELEASE;
                if (!iwlwifi_mod_params.sw_crypto)
-                       hw->wiphy->wowlan.flags |=
+                       priv->wowlan_support.flags |=
                                WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                WIPHY_WOWLAN_GTK_REKEY_FAILURE;
 
-               hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
-               hw->wiphy->wowlan.pattern_min_len =
+               priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
+               priv->wowlan_support.pattern_min_len =
                                        IWLAGN_WOWLAN_MIN_PATTERN_LEN;
-               hw->wiphy->wowlan.pattern_max_len =
+               priv->wowlan_support.pattern_max_len =
                                        IWLAGN_WOWLAN_MAX_PATTERN_LEN;
+               hw->wiphy->wowlan = &priv->wowlan_support;
        }
 #endif
 
index 68f7546..7aa9c8d 100644 (file)
@@ -1859,14 +1859,9 @@ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
                return pos;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log)
                size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
                        ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#else
-       size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
-               ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
-#endif
        IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
                size);
 
@@ -1910,10 +1905,8 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
        unsigned int reload_msec;
        unsigned long reload_jiffies;
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(IWL_DL_FW_ERRORS))
                iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
-#endif
 
        /* uCode is no longer loaded. */
        priv->ucode_loaded = false;
index a193832..0189b90 100644 (file)
@@ -237,6 +237,7 @@ struct iwl_cfg {
 /*
  * This list declares the config structures for all devices.
  */
+#if IS_ENABLED(CONFIG_IWLDVM)
 extern const struct iwl_cfg iwl5300_agn_cfg;
 extern const struct iwl_cfg iwl5100_agn_cfg;
 extern const struct iwl_cfg iwl5350_agn_cfg;
@@ -278,11 +279,14 @@ extern const struct iwl_cfg iwl6035_2agn_cfg;
 extern const struct iwl_cfg iwl105_bgn_cfg;
 extern const struct iwl_cfg iwl105_bgn_d_cfg;
 extern const struct iwl_cfg iwl135_bgn_cfg;
+#endif /* CONFIG_IWLDVM */
+#if IS_ENABLED(CONFIG_IWLMVM)
 extern const struct iwl_cfg iwl7260_2ac_cfg;
 extern const struct iwl_cfg iwl7260_2n_cfg;
 extern const struct iwl_cfg iwl7260_n_cfg;
 extern const struct iwl_cfg iwl3160_2ac_cfg;
 extern const struct iwl_cfg iwl3160_2n_cfg;
 extern const struct iwl_cfg iwl3160_n_cfg;
+#endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index 8cf5db7..7edb851 100644 (file)
 
 static inline bool iwl_have_debug_level(u32 level)
 {
+#ifdef CONFIG_IWLWIFI_DEBUG
        return iwlwifi_mod_params.debug_level & level;
+#else
+       return false;
+#endif
 }
 
 void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace,
index 7d14509..429337a 100644 (file)
@@ -62,8 +62,7 @@
 
 #ifndef __iwl_drv_h__
 #define __iwl_drv_h__
-
-#include <linux/module.h>
+#include <linux/export.h>
 
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
index 36dfe09..d4ad505 100644 (file)
@@ -115,7 +115,9 @@ struct iwl_mod_params {
        int led_mode;
        bool power_save;
        int power_level;
+#ifdef CONFIG_IWLWIFI_DEBUG
        u32 debug_level;
+#endif
        int ant_coupling;
        bool bt_ch_announce;
        bool auto_agg;
index 25745da..1a405ae 100644 (file)
@@ -92,20 +92,16 @@ struct iwl_phy_db_entry {
 struct iwl_phy_db {
        struct iwl_phy_db_entry cfg;
        struct iwl_phy_db_entry calib_nch;
-       struct iwl_phy_db_entry calib_ch;
        struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
        struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
 
-       u32 channel_num;
-       u32 channel_size;
-
        struct iwl_trans *trans;
 };
 
 enum iwl_phy_db_section_type {
        IWL_PHY_DB_CFG = 1,
        IWL_PHY_DB_CALIB_NCH,
-       IWL_PHY_DB_CALIB_CH,
+       IWL_PHY_DB_UNUSED,
        IWL_PHY_DB_CALIB_CHG_PAPD,
        IWL_PHY_DB_CALIB_CHG_TXP,
        IWL_PHY_DB_MAX
@@ -169,8 +165,6 @@ iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
                return &phy_db->cfg;
        case IWL_PHY_DB_CALIB_NCH:
                return &phy_db->calib_nch;
-       case IWL_PHY_DB_CALIB_CH:
-               return &phy_db->calib_ch;
        case IWL_PHY_DB_CALIB_CHG_PAPD:
                if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
                        return NULL;
@@ -208,7 +202,6 @@ void iwl_phy_db_free(struct iwl_phy_db *phy_db)
 
        iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
        iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
-       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
        for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
                iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
        for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
@@ -248,13 +241,6 @@ int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
 
        entry->size = size;
 
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               phy_db->channel_num =
-                       le32_to_cpup((__le32 *)phy_db_notif->data);
-               phy_db->channel_size =
-                       (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
-       }
-
        IWL_DEBUG_INFO(phy_db->trans,
                       "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
                       __func__, __LINE__, type, size);
@@ -328,10 +314,7 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
                                u32 type, u8 **data, u16 *size, u16 ch_id)
 {
        struct iwl_phy_db_entry *entry;
-       u32 channel_num;
-       u32 channel_size;
        u16 ch_group_id = 0;
-       u16 index;
 
        if (!phy_db)
                return -EINVAL;
@@ -346,21 +329,8 @@ int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
        if (!entry)
                return -EINVAL;
 
-       if (type == IWL_PHY_DB_CALIB_CH) {
-               index = ch_id_to_ch_index(ch_id);
-               channel_num = phy_db->channel_num;
-               channel_size = phy_db->channel_size;
-               if (index >= channel_num) {
-                       IWL_ERR(phy_db->trans, "Wrong channel number %d\n",
-                               ch_id);
-                       return -EINVAL;
-               }
-               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
-               *size = channel_size;
-       } else {
-               *data = entry->data;
-               *size = entry->size;
-       }
+       *data = entry->data;
+       *size = entry->size;
 
        IWL_DEBUG_INFO(phy_db->trans,
                       "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
@@ -413,6 +383,9 @@ static int iwl_phy_db_send_all_channel_groups(
                if (!entry)
                        return -EINVAL;
 
+               if (WARN_ON_ONCE(!entry->size))
+                       continue;
+
                /* Send the requested PHY DB section */
                err = iwl_send_phy_db_cmd(phy_db,
                                          type,
index 7a2ef3f..8c49db0 100644 (file)
@@ -420,8 +420,7 @@ static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr)
        return cpu_to_le16(be16_to_cpu((__force __be16)check));
 }
 
-static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm,
-                                    struct ieee80211_vif *vif,
+static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif,
                                     struct cfg80211_wowlan_tcp *tcp,
                                     void *_pkt, u8 *mask,
                                     __le16 *pseudo_hdr_csum,
@@ -567,21 +566,21 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* SYN (TX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->syn_tx.data, NULL,
+               vif, tcp, cfg->syn_tx.data, NULL,
                &cfg->syn_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_SYN);
        cfg->syn_tx.info.tcp_payload_length = 0;
 
        /* SYN/ACK (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
+               vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask,
                &cfg->synack_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_SYNACK);
        cfg->synack_rx.info.tcp_payload_length = 0;
 
        /* KEEPALIVE/ACK (TX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->keepalive_tx.data, NULL,
+               vif, tcp, cfg->keepalive_tx.data, NULL,
                &cfg->keepalive_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_DATA);
        cfg->keepalive_tx.info.tcp_payload_length =
@@ -605,7 +604,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* ACK (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->keepalive_ack_rx.data,
+               vif, tcp, cfg->keepalive_ack_rx.data,
                cfg->keepalive_ack_rx.rx_mask,
                &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_ACK);
@@ -613,7 +612,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* WAKEUP (RX) */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
+               vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask,
                &cfg->wake_rx.info.tcp_pseudo_header_checksum,
                MVM_TCP_RX_WAKE);
        cfg->wake_rx.info.tcp_payload_length =
@@ -621,7 +620,7 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
 
        /* FIN */
        iwl_mvm_build_tcp_packet(
-               mvm, vif, tcp, cfg->fin_tx.data, NULL,
+               vif, tcp, cfg->fin_tx.data, NULL,
                &cfg->fin_tx.info.tcp_pseudo_header_checksum,
                MVM_TCP_TX_FIN);
        cfg->fin_tx.info.tcp_payload_length = 0;
index 6994232..700cce7 100644 (file)
@@ -228,10 +228,11 @@ struct iwl_tx_cmd {
        __le16 len;
        __le16 next_frame_len;
        __le32 tx_flags;
-       /* DRAM_SCRATCH_API_U_VER_1 */
-       u8 try_cnt;
-       u8 btkill_cnt;
-       __le16 reserved;
+       struct {
+               u8 try_cnt;
+               u8 btkill_cnt;
+               __le16 reserved;
+       } scratch; /* DRAM_SCRATCH_API_U_VER_1 */
        __le32 rate_n_flags;
        u8 sta_id;
        u8 sec_ctl;
index 46c7c05..273b0cc 100644 (file)
@@ -193,14 +193,11 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
 u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif)
 {
-       u32 qmask, ac;
+       u32 qmask = 0, ac;
 
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
                return BIT(IWL_MVM_OFFCHANNEL_QUEUE);
 
-       qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
-               BIT(vif->cab_queue) : 0;
-
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
                if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
                        qmask |= BIT(vif->hw_queue[ac]);
index 2ed296c..e08683b 100644 (file)
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-                       BIT(NL80211_IFTYPE_AP),
+               .types = BIT(NL80211_IFTYPE_STATION),
        },
        {
                .max = 1,
-               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               .types = BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
                        BIT(NL80211_IFTYPE_P2P_GO),
        },
        {
@@ -236,20 +236,20 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
-               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                         WIPHY_WOWLAN_DISCONNECT |
-                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                   WIPHY_WOWLAN_DISCONNECT |
+                                   WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                   WIPHY_WOWLAN_RFKILL_RELEASE;
                if (!iwlwifi_mod_params.sw_crypto)
-                       hw->wiphy->wowlan.flags |=
-                               WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                               WIPHY_WOWLAN_GTK_REKEY_FAILURE |
-                               WIPHY_WOWLAN_4WAY_HANDSHAKE;
-
-               hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
-               hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
-               hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
-               hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+                       mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                                            WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+                                            WIPHY_WOWLAN_4WAY_HANDSHAKE;
+
+               mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
+               mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
+               mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+               mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
+               hw->wiphy->wowlan = &mvm->wowlan;
        }
 #endif
 
@@ -651,8 +651,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                 * By now, all the AC queues are empty. The AGG queues are
                 * empty too. We already got all the Tx responses for all the
                 * packets in the queues. The drain work can have been
-                * triggered. Flush it. This work item takes the mutex, so kill
-                * it before we take it.
+                * triggered. Flush it.
                 */
                flush_work(&mvm->sta_drained_wk);
        }
@@ -778,7 +777,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                ret = iwl_mvm_power_update_mode(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
-       } else if (changes & BSS_CHANGED_DTIM_PERIOD) {
+       } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
                 * remove the session protection.
index 4e10aae..c7409f1 100644 (file)
@@ -458,6 +458,7 @@ struct iwl_mvm {
        struct ieee80211_vif *p2p_device_vif;
 
 #ifdef CONFIG_PM_SLEEP
+       struct wiphy_wowlan_support wowlan;
        int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        bool d3_test_active;
@@ -689,16 +690,11 @@ void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void
 iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd);
-int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm);
 #else
 static inline void
 iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
                                         struct iwl_beacon_filter_cmd *cmd)
 {}
-static inline int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm)
-{
-       return 0;
-}
 #endif
 int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
                                 struct ieee80211_vif *vif);
index 2278858..62fe520 100644 (file)
@@ -229,9 +229,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
                if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
                        mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
 
-       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
-               mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue);
-
        /* for HW restart - need to reset the seq_number etc... */
        memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
 
@@ -1292,17 +1289,11 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        struct iwl_mvm_add_sta_cmd cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
-               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
-               .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE),
+               .station_flags_msk = cpu_to_le32(STA_FLG_PS),
                .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
        };
        int ret;
 
-       /*
-        * Same modify mask for sleep_tx_count and sleep_state_flags but this
-        * should be fine since if we set the STA as "awake", then
-        * sleep_tx_count is not relevant.
-        */
        ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
index 3efa0a0..94b265e 100644 (file)
@@ -250,7 +250,6 @@ enum iwl_mvm_agg_state {
  *     the first packet to be sent in legacy HW queue in Tx AGG stop flow.
  *     Basically when next_reclaimed reaches ssn, we can tell mac80211 that
  *     we are ready to finish the Tx AGG stop / start flow.
- * @wait_for_ba: Expect block-ack before next Tx reply
  */
 struct iwl_mvm_tid_data {
        u16 seq_number;
@@ -260,7 +259,6 @@ struct iwl_mvm_tid_data {
        enum iwl_mvm_agg_state state;
        u16 txq_id;
        u16 ssn;
-       bool wait_for_ba;
 };
 
 /**
index b9ba4e7..f0e96a9 100644 (file)
@@ -408,7 +408,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
        IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
                     tid, txq_id, seq_number);
 
-       /* NOTE: aggregation will need changes here (for txq id) */
        if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
                goto drop_unlock_sta;
 
@@ -610,8 +609,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                    !(info->flags & IEEE80211_TX_STAT_ACK))
                        info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
 
-               /* W/A FW bug: seq_ctl is wrong when the queue is flushed */
-               if (status == TX_STATUS_FAIL_FIFO_FLUSHED) {
+               /* W/A FW bug: seq_ctl is wrong when the status isn't success */
+               if (status != TX_STATUS_SUCCESS) {
                        struct ieee80211_hdr *hdr = (void *)skb->data;
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
                }
index db7bdd3..81f3ea5 100644 (file)
@@ -78,6 +78,7 @@
 
 /* Hardware specific file defines the PCI IDs table for that hardware module */
 static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
+#if IS_ENABLED(CONFIG_IWLDVM)
        {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
        {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
        {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
@@ -253,7 +254,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
+#endif /* CONFIG_IWLDVM */
 
+#if IS_ENABLED(CONFIG_IWLMVM)
 /* 7000 Series */
        {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)},
@@ -304,6 +307,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)},
        {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)},
+#endif /* CONFIG_IWLMVM */
 
        {0}
 };
index 148843e..b654dcd 100644 (file)
@@ -217,6 +217,7 @@ struct iwl_pcie_txq_scratch_buf {
  * @trans_pcie: pointer back to transport (for timer)
  * @need_update: indicates need to update read/write index
  * @active: stores if queue is active
+ * @ampdu: true if this queue is an ampdu queue for an specific RA/TID
  *
  * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
  * descriptors) and required locking structures.
@@ -232,6 +233,7 @@ struct iwl_txq {
        struct iwl_trans_pcie *trans_pcie;
        u8 need_update;
        u8 active;
+       bool ampdu;
 };
 
 static inline dma_addr_t
index 567e67a..3688dc5 100644 (file)
@@ -802,9 +802,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        u32 handled = 0;
        unsigned long flags;
        u32 i;
-#ifdef CONFIG_IWLWIFI_DEBUG
-       u32 inta_mask;
-#endif
 
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
@@ -826,14 +823,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
        inta = trans_pcie->inta;
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_have_debug_level(IWL_DL_ISR)) {
-               /* just for debug */
-               inta_mask = iwl_read32(trans, CSR_INT_MASK);
+       if (iwl_have_debug_level(IWL_DL_ISR))
                IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
-                             inta, inta_mask);
-       }
-#endif
+                             inta, iwl_read32(trans, CSR_INT_MASK));
 
        /* saved interrupt in inta variable now we can reset trans_pcie->inta */
        trans_pcie->inta = 0;
@@ -855,12 +847,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                goto out;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(IWL_DL_ISR)) {
                /* NIC fires this, but we don't use it, redundant with WAKEUP */
                if (inta & CSR_INT_BIT_SCD) {
-                       IWL_DEBUG_ISR(trans, "Scheduler finished to transmit "
-                                     "the frame/frames.\n");
+                       IWL_DEBUG_ISR(trans,
+                                     "Scheduler finished to transmit the frame/frames.\n");
                        isr_stats->sch++;
                }
 
@@ -870,7 +861,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                        isr_stats->alive++;
                }
        }
-#endif
+
        /* Safely ignore these bits for debug checks below */
        inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
 
@@ -1118,9 +1109,6 @@ static 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;
-#ifdef CONFIG_IWLWIFI_DEBUG
-       u32 inta_fh;
-#endif
 
        lockdep_assert_held(&trans_pcie->irq_lock);
 
@@ -1159,13 +1147,11 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
                return IRQ_HANDLED;
        }
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_have_debug_level(IWL_DL_ISR)) {
-               inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS);
-               IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, "
-                             "fh 0x%08x\n", inta, inta_mask, inta_fh);
-       }
-#endif
+       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 */
@@ -1198,7 +1184,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
 {
        struct iwl_trans *trans = data;
        struct iwl_trans_pcie *trans_pcie;
-       u32 inta, inta_mask;
+       u32 inta;
        u32 val = 0;
        u32 read;
        unsigned long flags;
@@ -1226,7 +1212,6 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
         * If we have something to service, the tasklet 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);
 
        /* Ignore interrupt if there's nothing in NIC to service.
@@ -1271,8 +1256,11 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
                val |= 0x8000;
 
        inta = (0xff & val) | ((0xff00 & val) << 16);
-       IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
-                     inta, inta_mask, val);
+       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;
index f65da19..c47c921 100644 (file)
@@ -576,10 +576,16 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
 
        spin_lock_bh(&txq->lock);
        while (q->write_ptr != q->read_ptr) {
+               IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
+                                  txq_id, q->read_ptr);
                iwl_pcie_txq_free_tfd(trans, txq);
                q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
        }
+       txq->active = false;
        spin_unlock_bh(&txq->lock);
+
+       /* just in case - this queue may have been stopped */
+       iwl_wake_queue(trans, txq);
 }
 
 /*
@@ -927,6 +933,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        spin_lock_bh(&txq->lock);
 
+       if (!txq->active) {
+               IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n",
+                                   txq_id, ssn);
+               goto out;
+       }
+
        if (txq->q.read_ptr == tfd_num)
                goto out;
 
@@ -1073,6 +1085,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
 
                /* enable aggregations for the queue */
                iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+               trans_pcie->txq[txq_id].ampdu = true;
        } else {
                /*
                 * disable aggregations for the queue, this will also make the
@@ -1107,6 +1120,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
                       (fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
                       (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
                       SCD_QUEUE_STTS_REG_MSK);
+       trans_pcie->txq[txq_id].active = true;
        IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n",
                            txq_id, fifo, ssn & 0xff);
 }
@@ -1129,6 +1143,7 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
                            ARRAY_SIZE(zero_val));
 
        iwl_pcie_txq_unmap(trans, txq_id);
+       trans_pcie->txq[txq_id].ampdu = false;
 
        IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id);
 }
@@ -1599,7 +1614,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        u8 wait_write_ptr = 0;
        __le16 fc = hdr->frame_control;
        u8 hdr_len = ieee80211_hdrlen(fc);
-       u16 __maybe_unused wifi_seq;
+       u16 wifi_seq;
 
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
@@ -1616,13 +1631,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         * the BA.
         * Check here that the packets are in the right place on the ring.
         */
-#ifdef CONFIG_IWLWIFI_DEBUG
        wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
-       WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) &&
-                 ((wifi_seq & 0xff) != q->write_ptr),
+       WARN_ONCE(trans_pcie->txq[txq_id].ampdu &&
+                 (wifi_seq & 0xff) != q->write_ptr,
                  "Q: %d WiFi Seq %d tfdNum %d",
                  txq_id, wifi_seq, q->write_ptr);
-#endif
 
        /* Set up driver data for this TFD */
        txq->entries[q->write_ptr].skb = skb;
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c
new file mode 100644 (file)
index 0000000..8d68307
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11h
+ *
+ * Copyright (C) 2013, 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 by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "main.h"
+#include "fw.h"
+
+
+/* This function appends 11h info to a buffer while joining an
+ * infrastructure BSS
+ */
+static void
+mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer,
+                              struct mwifiex_bssdescriptor *bss_desc)
+{
+       struct mwifiex_ie_types_header *ie_header;
+       struct mwifiex_ie_types_pwr_capability *cap;
+       struct mwifiex_ie_types_local_pwr_constraint *constraint;
+       struct ieee80211_supported_band *sband;
+       u8 radio_type;
+       int i;
+
+       if (!buffer || !(*buffer))
+               return;
+
+       radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
+       sband = priv->wdev->wiphy->bands[radio_type];
+
+       cap = (struct mwifiex_ie_types_pwr_capability *)*buffer;
+       cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
+       cap->header.len = cpu_to_le16(2);
+       cap->min_pwr = 0;
+       cap->max_pwr = 0;
+       *buffer += sizeof(*cap);
+
+       constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer;
+       constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
+       constraint->header.len = cpu_to_le16(2);
+       constraint->chan = bss_desc->channel;
+       constraint->constraint = bss_desc->local_constraint;
+       *buffer += sizeof(*constraint);
+
+       ie_header = (struct mwifiex_ie_types_header *)*buffer;
+       ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+       ie_header->len  = cpu_to_le16(2 * sband->n_channels + 2);
+       *buffer += sizeof(*ie_header);
+       *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
+       *(*buffer)++ = 2 * sband->n_channels;
+       for (i = 0; i < sband->n_channels; i++) {
+               *(*buffer)++ = ieee80211_frequency_to_channel(
+                                       sband->channels[i].center_freq);
+               *(*buffer)++ = 1; /* one channel in the subband */
+       }
+}
+
+/* Enable or disable the 11h extensions in the firmware */
+static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
+{
+       u32 enable = flag;
+
+       return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB,
+                                    HostCmd_ACT_GEN_SET, DOT11H_I, &enable);
+}
+
+/* This functions processes TLV buffer for a pending BSS Join command.
+ *
+ * Activate 11h functionality in the firmware if the spectrum management
+ * capability bit is found in the network we are joining. Also, necessary
+ * TLVs are set based on requested network's 11h capability.
+ */
+void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
+                             struct mwifiex_bssdescriptor *bss_desc)
+{
+       if (bss_desc->sensed_11h) {
+               /* Activate 11h functions in firmware, turns on capability
+                * bit
+                */
+               mwifiex_11h_activate(priv, true);
+               bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+               mwifiex_11h_process_infra_join(priv, buffer, bss_desc);
+       } else {
+               /* Deactivate 11h functions in the firmware */
+               mwifiex_11h_activate(priv, false);
+               bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
+       }
+}
index ecf2846..a42a506 100644 (file)
@@ -40,6 +40,7 @@ mwifiex-y += sta_rx.o
 mwifiex-y += uap_txrx.o
 mwifiex-y += cfg80211.o
 mwifiex-y += ethtool.o
+mwifiex-y += 11h.o
 mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
 obj-$(CONFIG_MWIFIEX) += mwifiex.o
 
index 00a8281..ef5fa89 100644 (file)
@@ -20,6 +20,9 @@
 #include "cfg80211.h"
 #include "main.h"
 
+static char *reg_alpha2;
+module_param(reg_alpha2, charp, 0);
+
 static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
                .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
@@ -2475,6 +2478,27 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
 #endif
 };
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
+       .flags = WIPHY_WOWLAN_MAGIC_PKT,
+       .n_patterns = MWIFIEX_MAX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+       .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+#endif
+
+static bool mwifiex_is_valid_alpha2(const char *alpha2)
+{
+       if (!alpha2 || strlen(alpha2) != 2)
+               return false;
+
+       if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+               return true;
+
+       return false;
+}
+
 /*
  * This function registers the device with CFG802.11 subsystem.
  *
@@ -2527,16 +2551,13 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                        WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
                        WIPHY_FLAG_AP_UAPSD |
                        WIPHY_FLAG_CUSTOM_REGULATORY |
+                       WIPHY_FLAG_STRICT_REGULATORY |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
        wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
 
 #ifdef CONFIG_PM
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
-       wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS;
-       wiphy->wowlan.pattern_min_len = 1;
-       wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN;
-       wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN;
+       wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
 
        wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
@@ -2568,10 +2589,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                wiphy_free(wiphy);
                return ret;
        }
-       country_code = mwifiex_11d_code_2_region(priv->adapter->region_code);
-       if (country_code)
-               dev_info(adapter->dev,
-                        "ignoring F/W country code %2.2s\n", country_code);
+
+       if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) {
+               wiphy_info(wiphy, "driver hint alpha2: %2.2s\n", reg_alpha2);
+               regulatory_hint(wiphy, reg_alpha2);
+       } else {
+               country_code = mwifiex_11d_code_2_region(adapter->region_code);
+               if (country_code)
+                       wiphy_info(wiphy, "ignoring F/W country code %2.2s\n",
+                                  country_code);
+       }
 
        adapter->wiphy = wiphy;
        return ret;
index d6ada73..1b45aa5 100644 (file)
@@ -245,6 +245,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HT_BW_20    0
 #define HT_BW_40    1
 
+#define DFS_CHAN_MOVE_TIME      10000
+
 #define HostCmd_CMD_GET_HW_SPEC                       0x0003
 #define HostCmd_CMD_802_11_SCAN                       0x0006
 #define HostCmd_CMD_802_11_GET_LOG                    0x000b
@@ -438,6 +440,7 @@ enum P2P_MODES {
 #define EVENT_BW_CHANGE                 0x00000048
 #define EVENT_UAP_MIC_COUNTERMEASURES   0x0000004c
 #define EVENT_HOSTWAKE_STAIE           0x0000004d
+#define EVENT_CHANNEL_SWITCH_ANN        0x00000050
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 
 #define EVENT_ID_MASK                   0xffff
@@ -975,6 +978,7 @@ enum SNMP_MIB_INDEX {
        LONG_RETRY_LIM_I = 7,
        FRAG_THRESH_I = 8,
        DOT11D_I = 9,
+       DOT11H_I = 10,
 };
 
 #define MAX_SNMP_BUF_SIZE   128
@@ -1206,6 +1210,18 @@ struct host_cmd_ds_sta_deauth {
        __le16 reason;
 } __packed;
 
+struct mwifiex_ie_types_pwr_capability {
+       struct mwifiex_ie_types_header header;
+       s8 min_pwr;
+       s8 max_pwr;
+};
+
+struct mwifiex_ie_types_local_pwr_constraint {
+       struct mwifiex_ie_types_header header;
+       u8 chan;
+       u8 constraint;
+};
+
 struct mwifiex_ie_types_wmm_param_set {
        struct mwifiex_ie_types_header header;
        u8 wmm_ie[1];
index c7f11c0..caaf4bd 100644 (file)
@@ -52,84 +52,6 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
        return 0;
 }
 
-static void scan_delay_timer_fn(unsigned long data)
-{
-       struct mwifiex_private *priv = (struct mwifiex_private *)data;
-       struct mwifiex_adapter *adapter = priv->adapter;
-       struct cmd_ctrl_node *cmd_node, *tmp_node;
-       unsigned long flags;
-
-       if (adapter->surprise_removed)
-               return;
-
-       if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
-               /*
-                * Abort scan operation by cancelling all pending scan
-                * commands
-                */
-               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-               list_for_each_entry_safe(cmd_node, tmp_node,
-                                        &adapter->scan_pending_q, list) {
-                       list_del(&cmd_node->list);
-                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
-               }
-               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
-
-               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
-               adapter->scan_processing = false;
-               adapter->scan_delay_cnt = 0;
-               adapter->empty_tx_q_cnt = 0;
-               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
-
-               if (priv->scan_request) {
-                       dev_dbg(adapter->dev, "info: aborting scan\n");
-                       cfg80211_scan_done(priv->scan_request, 1);
-                       priv->scan_request = NULL;
-               } else {
-                       priv->scan_aborting = false;
-                       dev_dbg(adapter->dev, "info: scan already aborted\n");
-               }
-               goto done;
-       }
-
-       if (!atomic_read(&priv->adapter->is_tx_received)) {
-               adapter->empty_tx_q_cnt++;
-               if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
-                       /*
-                        * No Tx traffic for 200msec. Get scan command from
-                        * scan pending queue and put to cmd pending queue to
-                        * resume scan operation
-                        */
-                       adapter->scan_delay_cnt = 0;
-                       adapter->empty_tx_q_cnt = 0;
-                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
-                       cmd_node = list_first_entry(&adapter->scan_pending_q,
-                                                   struct cmd_ctrl_node, list);
-                       list_del(&cmd_node->list);
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
-
-                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
-                                                       true);
-                       queue_work(adapter->workqueue, &adapter->main_work);
-                       goto done;
-               }
-       } else {
-               adapter->empty_tx_q_cnt = 0;
-       }
-
-       /* Delay scan operation further by 20msec */
-       mod_timer(&priv->scan_delay_timer, jiffies +
-                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
-       adapter->scan_delay_cnt++;
-
-done:
-       if (atomic_read(&priv->adapter->is_tx_received))
-               atomic_set(&priv->adapter->is_tx_received, false);
-
-       return;
-}
-
 /*
  * This function initializes the private structure and sets default
  * values to the members.
@@ -211,8 +133,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
 
        priv->scan_block = false;
 
-       setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn,
-                   (unsigned long)priv);
+       priv->csa_chan = 0;
+       priv->csa_expire_time = 0;
 
        return mwifiex_add_bss_prio_tbl(priv);
 }
index 122175a..1c8a771 100644 (file)
@@ -534,6 +534,8 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
 
        mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc);
 
+       mwifiex_11h_process_join(priv, &pos, bss_desc);
+
        cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN);
 
        /* Set the Capability info at last */
index 5bc7ef8..e15ab72 100644 (file)
@@ -28,6 +28,84 @@ const char driver_version[] = "mwifiex " VERSION " (%s) ";
 static char *cal_data_cfg;
 module_param(cal_data_cfg, charp, 0);
 
+static void scan_delay_timer_fn(unsigned long data)
+{
+       struct mwifiex_private *priv = (struct mwifiex_private *)data;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmd_node, *tmp_node;
+       unsigned long flags;
+
+       if (adapter->surprise_removed)
+               return;
+
+       if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
+               /*
+                * Abort scan operation by cancelling all pending scan
+                * commands
+                */
+               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+               list_for_each_entry_safe(cmd_node, tmp_node,
+                                        &adapter->scan_pending_q, list) {
+                       list_del(&cmd_node->list);
+                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               }
+               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->scan_processing = false;
+               adapter->scan_delay_cnt = 0;
+               adapter->empty_tx_q_cnt = 0;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+               if (priv->scan_request) {
+                       dev_dbg(adapter->dev, "info: aborting scan\n");
+                       cfg80211_scan_done(priv->scan_request, 1);
+                       priv->scan_request = NULL;
+               } else {
+                       priv->scan_aborting = false;
+                       dev_dbg(adapter->dev, "info: scan already aborted\n");
+               }
+               goto done;
+       }
+
+       if (!atomic_read(&priv->adapter->is_tx_received)) {
+               adapter->empty_tx_q_cnt++;
+               if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) {
+                       /*
+                        * No Tx traffic for 200msec. Get scan command from
+                        * scan pending queue and put to cmd pending queue to
+                        * resume scan operation
+                        */
+                       adapter->scan_delay_cnt = 0;
+                       adapter->empty_tx_q_cnt = 0;
+                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+                       cmd_node = list_first_entry(&adapter->scan_pending_q,
+                                                   struct cmd_ctrl_node, list);
+                       list_del(&cmd_node->list);
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              flags);
+
+                       mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
+                                                       true);
+                       queue_work(adapter->workqueue, &adapter->main_work);
+                       goto done;
+               }
+       } else {
+               adapter->empty_tx_q_cnt = 0;
+       }
+
+       /* Delay scan operation further by 20msec */
+       mod_timer(&priv->scan_delay_timer, jiffies +
+                 msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC));
+       adapter->scan_delay_cnt++;
+
+done:
+       if (atomic_read(&priv->adapter->is_tx_received))
+               atomic_set(&priv->adapter->is_tx_received, false);
+
+       return;
+}
+
 /*
  * This function registers the device and performs all the necessary
  * initializations.
@@ -75,6 +153,10 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
 
                adapter->priv[i]->adapter = adapter;
                adapter->priv_num++;
+
+               setup_timer(&adapter->priv[i]->scan_delay_timer,
+                           scan_delay_timer_fn,
+                           (unsigned long)adapter->priv[i]);
        }
        mwifiex_init_lock_list(adapter);
 
@@ -587,9 +669,8 @@ static void mwifiex_set_multicast_list(struct net_device *dev)
                mcast_list.mode = MWIFIEX_ALL_MULTI_MODE;
        } else {
                mcast_list.mode = MWIFIEX_MULTICAST_MODE;
-               if (netdev_mc_count(dev))
-                       mcast_list.num_multicast_addr =
-                               mwifiex_copy_mcast_addr(&mcast_list, dev);
+               mcast_list.num_multicast_addr =
+                       mwifiex_copy_mcast_addr(&mcast_list, dev);
        }
        mwifiex_request_set_multicast_list(priv, &mcast_list);
 }
index 0832c24..3da73d3 100644 (file)
@@ -309,6 +309,9 @@ struct mwifiex_bssdescriptor {
        u16 wapi_offset;
        u8 *beacon_buf;
        u32 beacon_buf_size;
+       u8 sensed_11h;
+       u8 local_constraint;
+       u8 chan_sw_ie_present;
 };
 
 struct mwifiex_current_bss_params {
@@ -510,6 +513,8 @@ struct mwifiex_private {
        u32 mgmt_frame_mask;
        struct mwifiex_roc_cfg roc_cfg;
        bool scan_aborting;
+       u8 csa_chan;
+       unsigned long csa_expire_time;
 };
 
 enum mwifiex_ba_status {
@@ -1018,6 +1023,24 @@ static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
        return (*(u32 *)skb->data == PKT_TYPE_MGMT);
 }
 
+/* This function retrieves channel closed for operation by Channel
+ * Switch Announcement.
+ */
+static inline u8
+mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv)
+{
+       if (!priv->csa_chan)
+               return 0;
+
+       /* Clear csa channel, if DFS channel move time has passed */
+       if (jiffies > priv->csa_expire_time) {
+               priv->csa_chan = 0;
+               priv->csa_expire_time = 0;
+       }
+
+       return priv->csa_chan;
+}
+
 int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
                             u32 func_init_shutdown);
 int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8);
@@ -1119,6 +1142,10 @@ u8 *mwifiex_11d_code_2_region(u8 code);
 void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
                              struct mwifiex_sta_node *node);
 
+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);
+
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
 #ifdef CONFIG_DEBUG_FS
index 801b6b7..c447d9b 100644 (file)
@@ -391,6 +391,12 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv,
                return 0;
        }
 
+       if (bss_desc->chan_sw_ie_present) {
+               dev_err(adapter->dev,
+                       "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
+               return -1;
+       }
+
        if (mwifiex_is_bss_wapi(priv, bss_desc)) {
                dev_dbg(adapter->dev, "info: return success for WAPI AP\n");
                return 0;
@@ -569,6 +575,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                return -1;
        }
 
+       /* Check csa channel expiry before preparing scan list */
+       mwifiex_11h_get_csa_closed_channel(priv);
+
        chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
 
        /* Set the temp channel struct pointer to the start of the desired
@@ -598,6 +607,11 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv,
                while (tlv_idx < max_chan_per_scan &&
                       tmp_chan_list->chan_number && !done_early) {
 
+                       if (tmp_chan_list->chan_number == priv->csa_chan) {
+                               tmp_chan_list++;
+                               continue;
+                       }
+
                        dev_dbg(priv->adapter->dev,
                                "info: Scan: Chan(%3d), Radio(%d),"
                                " Mode(%d, %d), Dur(%d)\n",
@@ -1169,6 +1183,19 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
                        bss_entry->erp_flags = *(current_ptr + 2);
                        break;
 
+               case WLAN_EID_PWR_CONSTRAINT:
+                       bss_entry->local_constraint = *(current_ptr + 2);
+                       bss_entry->sensed_11h = true;
+                       break;
+
+               case WLAN_EID_CHANNEL_SWITCH:
+                       bss_entry->chan_sw_ie_present = true;
+               case WLAN_EID_PWR_CAPABILITY:
+               case WLAN_EID_TPC_REPORT:
+               case WLAN_EID_QUIET:
+                       bss_entry->sensed_11h = true;
+                   break;
+
                case WLAN_EID_EXT_SUPP_RATES:
                        /*
                         * Only process extended supported rate
@@ -1575,6 +1602,9 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                goto check_next_scan;
        }
 
+       /* Check csa channel expiry before parsing scan response */
+       mwifiex_11h_get_csa_closed_channel(priv);
+
        bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
        dev_dbg(adapter->dev, "info: SCAN_RESP: bss_descript_size %d\n",
                bytes_left);
@@ -1727,6 +1757,13 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
                        struct ieee80211_channel *chan;
                        u8 band;
 
+                       /* Skip entry if on csa closed channel */
+                       if (channel == priv->csa_chan) {
+                               dev_dbg(adapter->dev,
+                                       "Dropping entry on csa closed channel\n");
+                               continue;
+                       }
+
                        band = BAND_G;
                        if (chan_band_tlv) {
                                chan_band =
index 41aafc7..ea265ec 100644 (file)
@@ -427,6 +427,17 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
 
                break;
 
+       case EVENT_CHANNEL_SWITCH_ANN:
+               dev_dbg(adapter->dev, "event: Channel Switch Announcement\n");
+               priv->csa_expire_time =
+                               jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
+               priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel;
+               ret = mwifiex_send_cmd_async(priv,
+                       HostCmd_CMD_802_11_DEAUTHENTICATE,
+                       HostCmd_ACT_GEN_SET, 0,
+                       priv->curr_bss_params.bss_descriptor.mac_address);
+               break;
+
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index 1a8a19d..206c3e0 100644 (file)
@@ -104,16 +104,14 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
                } else {
                        priv->curr_pkt_filter &=
                                ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE;
-                       if (mcast_list->num_multicast_addr) {
-                               dev_dbg(priv->adapter->dev,
-                                       "info: Set multicast list=%d\n",
-                                      mcast_list->num_multicast_addr);
-                               /* Send multicast addresses to firmware */
-                               ret = mwifiex_send_cmd_async(priv,
-                                       HostCmd_CMD_MAC_MULTICAST_ADR,
-                                       HostCmd_ACT_GEN_SET, 0,
-                                       mcast_list);
-                       }
+                       dev_dbg(priv->adapter->dev,
+                               "info: Set multicast list=%d\n",
+                               mcast_list->num_multicast_addr);
+                       /* Send multicast addresses to firmware */
+                       ret = mwifiex_send_cmd_async(priv,
+                               HostCmd_CMD_MAC_MULTICAST_ADR,
+                               HostCmd_ACT_GEN_SET, 0,
+                               mcast_list);
                }
        }
        dev_dbg(priv->adapter->dev,
@@ -180,6 +178,9 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
         */
        bss_desc->disable_11ac = true;
 
+       if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
+               bss_desc->sensed_11h = true;
+
        return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
 }
 
@@ -257,30 +258,37 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
        }
 
        if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+               u8 config_bands;
+
                /* Infra mode */
                ret = mwifiex_deauthenticate(priv, NULL);
                if (ret)
                        goto done;
 
-               if (bss_desc) {
-                       u8 config_bands = 0;
+               if (!bss_desc)
+                       return -1;
 
-                       if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band)
-                           == HostCmd_SCAN_RADIO_TYPE_BG)
-                               config_bands = BAND_B | BAND_G | BAND_GN |
-                                              BAND_GAC;
-                       else
-                               config_bands = BAND_A | BAND_AN | BAND_AAC;
+               if (mwifiex_band_to_radio_type(bss_desc->bss_band) ==
+                                               HostCmd_SCAN_RADIO_TYPE_BG)
+                       config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC;
+               else
+                       config_bands = BAND_A | BAND_AN | BAND_AAC;
 
-                       if (!((config_bands | adapter->fw_bands) &
-                             ~adapter->fw_bands))
-                               adapter->config_bands = config_bands;
-               }
+               if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
+                       adapter->config_bands = config_bands;
 
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
                if (ret)
                        goto done;
 
+               if (mwifiex_11h_get_csa_closed_channel(priv) ==
+                                                       (u8)bss_desc->channel) {
+                       dev_err(adapter->dev,
+                               "Attempt to reconnect on csa closed chan(%d)\n",
+                               bss_desc->channel);
+                       goto done;
+               }
+
                dev_dbg(adapter->dev, "info: SSID found in scan list ... "
                                      "associating...\n");
 
index 4be3d33..944e884 100644 (file)
@@ -37,6 +37,9 @@
 /* Offset for TOS field in the IP header */
 #define IPTOS_OFFSET 5
 
+static bool enable_tx_amsdu;
+module_param(enable_tx_amsdu, bool, 0644);
+
 /* WMM information IE */
 static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07,
        0x00, 0x50, 0xf2, 0x02,
@@ -1233,7 +1236,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
                                mwifiex_send_delba(priv, tid_del, ra, 1);
                        }
                }
-               if (mwifiex_is_amsdu_allowed(priv, tid) &&
+               if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) &&
                    mwifiex_is_11n_aggragation_possible(priv, ptr,
                                                        adapter->tx_buf_size))
                        mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN,
index ea7231a..43f5b9f 100644 (file)
@@ -38,7 +38,7 @@ static int orinoco_pci_resume(struct pci_dev *pdev)
        struct net_device *dev = priv->ndev;
        int err;
 
-       pci_set_power_state(pdev, 0);
+       pci_set_power_state(pdev, PCI_D0);
        err = pci_enable_device(pdev);
        if (err) {
                printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
index 1f9cb55..bdfe637 100644 (file)
@@ -881,7 +881,8 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv,
 
        if (!upriv->udev) {
                dbg("Device disconnected");
-               return -ENODEV;
+               retval = -ENODEV;
+               goto exit;
        }
 
        if (upriv->read_urb->status != -EINPROGRESS)
index f714373..3d53a09 100644 (file)
@@ -1767,33 +1767,45 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
        .config                 = rt2400pci_config,
 };
 
-static const struct data_queue_desc rt2400pci_queue_rx = {
-       .entry_num              = 24,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2400pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 24;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_tx = {
-       .entry_num              = 24,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 24;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2400pci_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2400pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1801,11 +1813,7 @@ static const struct rt2x00_ops rt2400pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt2400pci_queue_rx,
-       .tx                     = &rt2400pci_queue_tx,
-       .bcn                    = &rt2400pci_queue_bcn,
-       .atim                   = &rt2400pci_queue_atim,
+       .queue_init             = rt2400pci_queue_init,
        .lib                    = &rt2400pci_rt2x00_ops,
        .hw                     = &rt2400pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 77e45b2..0ac5c58 100644 (file)
@@ -2056,33 +2056,45 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
        .config                 = rt2500pci_config,
 };
 
-static const struct data_queue_desc rt2500pci_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2500pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2500pci_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2500pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -2090,11 +2102,7 @@ static const struct rt2x00_ops rt2500pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt2500pci_queue_rx,
-       .tx                     = &rt2500pci_queue_tx,
-       .bcn                    = &rt2500pci_queue_bcn,
-       .atim                   = &rt2500pci_queue_atim,
+       .queue_init             = rt2500pci_queue_init,
        .lib                    = &rt2500pci_rt2x00_ops,
        .hw                     = &rt2500pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index a7f7b36..85acc79 100644 (file)
@@ -1867,33 +1867,45 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
        .config                 = rt2500usb_config,
 };
 
-static const struct data_queue_desc rt2500usb_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt2500usb_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_bcn = {
-       .entry_num              = 1,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb_bcn),
-};
+       case QID_BEACON:
+               queue->limit = 1;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn);
+               break;
 
-static const struct data_queue_desc rt2500usb_queue_atim = {
-       .entry_num              = 8,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_ATIM:
+               queue->limit = 8;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2500usb_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1901,11 +1913,7 @@ static const struct rt2x00_ops rt2500usb_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXD_DESC_SIZE,
-       .rx                     = &rt2500usb_queue_rx,
-       .tx                     = &rt2500usb_queue_tx,
-       .bcn                    = &rt2500usb_queue_bcn,
-       .atim                   = &rt2500usb_queue_atim,
+       .queue_init             = rt2500usb_queue_init,
        .lib                    = &rt2500usb_rt2x00_ops,
        .hw                     = &rt2500usb_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 330f1d2..7c74782 100644 (file)
@@ -1186,29 +1186,43 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static const struct data_queue_desc rt2800pci_queue_rx = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt2800pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->winfo_size = RXWI_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2800pci_queue_tx = {
-       .entry_num              = 64,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 64;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = TXWI_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt2800pci_queue_bcn = {
-       .entry_num              = 8,
-       .data_size              = 0, /* No DMA required for beacons */
-       .desc_size              = TXD_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->winfo_size = TXWI_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt2800pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -1217,10 +1231,7 @@ static const struct rt2x00_ops rt2800pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXWI_DESC_SIZE,
-       .rx                     = &rt2800pci_queue_rx,
-       .tx                     = &rt2800pci_queue_tx,
-       .bcn                    = &rt2800pci_queue_bcn,
+       .queue_init             = rt2800pci_queue_init,
        .lib                    = &rt2800pci_rt2x00_ops,
        .drv                    = &rt2800pci_rt2800_ops,
        .hw                     = &rt2800pci_mac80211_ops,
index c71a48d..7edd903 100644 (file)
@@ -849,85 +849,63 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
        .sta_remove             = rt2800_sta_remove,
 };
 
-static const struct data_queue_desc rt2800usb_queue_rx = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXINFO_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
-
-static const struct data_queue_desc rt2800usb_queue_tx = {
-       .entry_num              = 16,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
-
-static const struct data_queue_desc rt2800usb_queue_bcn = {
-       .entry_num              = 8,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt2800usb_queue_init(struct data_queue *queue)
+{
+       struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+       unsigned short txwi_size, rxwi_size;
 
-static const struct rt2x00_ops rt2800usb_ops = {
-       .name                   = KBUILD_MODNAME,
-       .drv_data_size          = sizeof(struct rt2800_drv_data),
-       .max_ap_intf            = 8,
-       .eeprom_size            = EEPROM_SIZE,
-       .rf_size                = RF_SIZE,
-       .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE,
-       .rx                     = &rt2800usb_queue_rx,
-       .tx                     = &rt2800usb_queue_tx,
-       .bcn                    = &rt2800usb_queue_bcn,
-       .lib                    = &rt2800usb_rt2x00_ops,
-       .drv                    = &rt2800usb_rt2800_ops,
-       .hw                     = &rt2800usb_mac80211_ops,
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-       .debugfs                = &rt2800_rt2x00debug,
-#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
-};
+       if (rt2x00_rt(rt2x00dev, RT5592)) {
+               txwi_size = TXWI_DESC_SIZE_5592;
+               rxwi_size = RXWI_DESC_SIZE_5592;
+       } else {
+               txwi_size = TXWI_DESC_SIZE;
+               rxwi_size = RXWI_DESC_SIZE;
+       }
 
-static const struct data_queue_desc rt2800usb_queue_rx_5592 = {
-       .entry_num              = 128,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = RXINFO_DESC_SIZE,
-       .winfo_size             = RXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 128;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = RXINFO_DESC_SIZE;
+               queue->winfo_size = rxwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2800usb_queue_tx_5592 = {
-       .entry_num              = 16,
-       .data_size              = AGGREGATION_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 16;
+               queue->data_size = AGGREGATION_SIZE;
+               queue->desc_size = TXINFO_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt2800usb_queue_bcn_5592 = {
-       .entry_num              = 8,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_DESC_SIZE,
-       .winfo_size             = TXWI_DESC_SIZE_5592,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_BEACON:
+               queue->limit = 8;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXINFO_DESC_SIZE;
+               queue->winfo_size = txwi_size;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
-static const struct rt2x00_ops rt2800usb_ops_5592 = {
+static const struct rt2x00_ops rt2800usb_ops = {
        .name                   = KBUILD_MODNAME,
        .drv_data_size          = sizeof(struct rt2800_drv_data),
        .max_ap_intf            = 8,
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592,
-       .rx                     = &rt2800usb_queue_rx_5592,
-       .tx                     = &rt2800usb_queue_tx_5592,
-       .bcn                    = &rt2800usb_queue_bcn_5592,
+       .queue_init             = rt2800usb_queue_init,
        .lib                    = &rt2800usb_rt2x00_ops,
        .drv                    = &rt2800usb_rt2800_ops,
        .hw                     = &rt2800usb_mac80211_ops,
@@ -1248,15 +1226,15 @@ static struct usb_device_id rt2800usb_device_table[] = {
 #endif
 #ifdef CONFIG_RT2800USB_RT55XX
        /* Arcadyan */
-       { USB_DEVICE(0x043e, 0x7a32), .driver_info = 5592 },
+       { USB_DEVICE(0x043e, 0x7a32) },
        /* AVM GmbH */
-       { USB_DEVICE(0x057c, 0x8501), .driver_info = 5592 },
+       { USB_DEVICE(0x057c, 0x8501) },
        /* D-Link DWA-160-B2 */
-       { USB_DEVICE(0x2001, 0x3c1a), .driver_info = 5592 },
+       { USB_DEVICE(0x2001, 0x3c1a) },
        /* Proware */
-       { USB_DEVICE(0x043e, 0x7a13), .driver_info = 5592 },
+       { USB_DEVICE(0x043e, 0x7a13) },
        /* Ralink */
-       { USB_DEVICE(0x148f, 0x5572), .driver_info = 5592 },
+       { USB_DEVICE(0x148f, 0x5572) },
 #endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
@@ -1361,9 +1339,6 @@ MODULE_LICENSE("GPL");
 static int rt2800usb_probe(struct usb_interface *usb_intf,
                           const struct usb_device_id *id)
 {
-       if (id->driver_info == 5592)
-               return rt2x00usb_probe(usb_intf, &rt2800usb_ops_5592);
-
        return rt2x00usb_probe(usb_intf, &rt2800usb_ops);
 }
 
index 7510723..ee3fc57 100644 (file)
@@ -648,11 +648,7 @@ struct rt2x00_ops {
        const unsigned int eeprom_size;
        const unsigned int rf_size;
        const unsigned int tx_queues;
-       const unsigned int extra_tx_headroom;
-       const struct data_queue_desc *rx;
-       const struct data_queue_desc *tx;
-       const struct data_queue_desc *bcn;
-       const struct data_queue_desc *atim;
+       void (*queue_init)(struct data_queue *queue);
        const struct rt2x00lib_ops *lib;
        const void *drv;
        const struct ieee80211_ops *hw;
@@ -1010,6 +1006,9 @@ struct rt2x00_dev {
         */
        struct list_head bar_list;
        spinlock_t bar_list_lock;
+
+       /* Extra TX headroom required for alignment purposes. */
+       unsigned int extra_tx_headroom;
 };
 
 struct rt2x00_bar_list_entry {
index 6a20172..f03e3bb 100644 (file)
@@ -334,7 +334,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
        /*
         * Remove the extra tx headroom from the skb.
         */
-       skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom);
+       skb_pull(entry->skb, rt2x00dev->extra_tx_headroom);
 
        /*
         * Signal that the TX descriptor is no longer in the skb.
@@ -1049,7 +1049,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
         */
        rt2x00dev->hw->extra_tx_headroom =
                max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM,
-                     rt2x00dev->ops->extra_tx_headroom);
+                     rt2x00dev->extra_tx_headroom);
 
        /*
         * Take TX headroom required for alignment into account.
@@ -1256,6 +1256,17 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
        rt2x00dev->hw->wiphy->n_iface_combinations = 1;
 }
 
+static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev)
+{
+       if (WARN_ON(!rt2x00dev->tx))
+               return 0;
+
+       if (rt2x00_is_usb(rt2x00dev))
+               return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size;
+
+       return rt2x00dev->tx[0].winfo_size;
+}
+
 /*
  * driver allocation handlers.
  */
@@ -1330,13 +1341,16 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
        if (retval)
                goto exit;
 
+       /* Cache TX headroom value */
+       rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev);
+
        /*
         * Determine which operating modes are supported, all modes
         * which require beaconing, depend on the availability of
         * beacon entries.
         */
        rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-       if (rt2x00dev->ops->bcn->entry_num > 0)
+       if (rt2x00dev->bcn->limit > 0)
                rt2x00dev->hw->wiphy->interface_modes |=
                    BIT(NL80211_IFTYPE_ADHOC) |
                    BIT(NL80211_IFTYPE_AP) |
index 5efbbbd..6c0a91f 100644 (file)
@@ -542,8 +542,8 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry,
        /*
         * Add the requested extra tx headroom in front of the skb.
         */
-       skb_push(entry->skb, rt2x00dev->ops->extra_tx_headroom);
-       memset(entry->skb->data, 0, rt2x00dev->ops->extra_tx_headroom);
+       skb_push(entry->skb, rt2x00dev->extra_tx_headroom);
+       memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom);
 
        /*
         * Call the driver's write_tx_data function, if it exists.
@@ -596,7 +596,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct ieee80211_bar *bar = (void *) (entry->skb->data +
-                                   rt2x00dev->ops->extra_tx_headroom);
+                                   rt2x00dev->extra_tx_headroom);
        struct rt2x00_bar_list_entry *bar_entry;
 
        if (likely(!ieee80211_is_back_req(bar->frame_control)))
@@ -1161,8 +1161,7 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
        }
 }
 
-static int rt2x00queue_alloc_entries(struct data_queue *queue,
-                                    const struct data_queue_desc *qdesc)
+static int rt2x00queue_alloc_entries(struct data_queue *queue)
 {
        struct queue_entry *entries;
        unsigned int entry_size;
@@ -1173,7 +1172,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
        /*
         * Allocate all queue entries.
         */
-       entry_size = sizeof(*entries) + qdesc->priv_size;
+       entry_size = sizeof(*entries) + queue->priv_size;
        entries = kcalloc(queue->limit, entry_size, GFP_KERNEL);
        if (!entries)
                return -ENOMEM;
@@ -1189,7 +1188,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
                entries[i].entry_idx = i;
                entries[i].priv_data =
                    QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit,
-                                           sizeof(*entries), qdesc->priv_size);
+                                           sizeof(*entries), queue->priv_size);
        }
 
 #undef QUEUE_ENTRY_PRIV_OFFSET
@@ -1231,23 +1230,22 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev)
        struct data_queue *queue;
        int status;
 
-       status = rt2x00queue_alloc_entries(rt2x00dev->rx, rt2x00dev->ops->rx);
+       status = rt2x00queue_alloc_entries(rt2x00dev->rx);
        if (status)
                goto exit;
 
        tx_queue_for_each(rt2x00dev, queue) {
-               status = rt2x00queue_alloc_entries(queue, rt2x00dev->ops->tx);
+               status = rt2x00queue_alloc_entries(queue);
                if (status)
                        goto exit;
        }
 
-       status = rt2x00queue_alloc_entries(rt2x00dev->bcn, rt2x00dev->ops->bcn);
+       status = rt2x00queue_alloc_entries(rt2x00dev->bcn);
        if (status)
                goto exit;
 
        if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) {
-               status = rt2x00queue_alloc_entries(rt2x00dev->atim,
-                                                  rt2x00dev->ops->atim);
+               status = rt2x00queue_alloc_entries(rt2x00dev->atim);
                if (status)
                        goto exit;
        }
@@ -1278,38 +1276,9 @@ void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev)
        }
 }
 
-static const struct data_queue_desc *
-rt2x00queue_get_qdesc_by_qid(struct rt2x00_dev *rt2x00dev,
-                            enum data_queue_qid qid)
-{
-       switch (qid) {
-       case QID_RX:
-               return rt2x00dev->ops->rx;
-
-       case QID_AC_BE:
-       case QID_AC_BK:
-       case QID_AC_VO:
-       case QID_AC_VI:
-               return rt2x00dev->ops->tx;
-
-       case QID_BEACON:
-               return rt2x00dev->ops->bcn;
-
-       case QID_ATIM:
-               return rt2x00dev->ops->atim;
-
-       default:
-               break;
-       }
-
-       return NULL;
-}
-
 static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
                             struct data_queue *queue, enum data_queue_qid qid)
 {
-       const struct data_queue_desc *qdesc;
-
        mutex_init(&queue->status_lock);
        spin_lock_init(&queue->tx_lock);
        spin_lock_init(&queue->index_lock);
@@ -1321,14 +1290,9 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
        queue->cw_min = 5;
        queue->cw_max = 10;
 
-       qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid);
-       BUG_ON(!qdesc);
+       rt2x00dev->ops->queue_init(queue);
 
-       queue->limit = qdesc->entry_num;
-       queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10);
-       queue->data_size = qdesc->data_size;
-       queue->desc_size = qdesc->desc_size;
-       queue->winfo_size = qdesc->winfo_size;
+       queue->threshold = DIV_ROUND_UP(queue->limit, 10);
 }
 
 int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev)
index 4a7b34e..ebe1172 100644 (file)
@@ -453,6 +453,7 @@ enum data_queue_flags {
  * @cw_max: The cw max value for outgoing frames (field ignored in RX queue).
  * @data_size: Maximum data size for the frames in this queue.
  * @desc_size: Hardware descriptor size for the data in this queue.
+ * @priv_size: Size of per-queue_entry private data.
  * @usb_endpoint: Device endpoint used for communication (USB only)
  * @usb_maxpacket: Max packet size for given endpoint (USB only)
  */
@@ -481,30 +482,12 @@ struct data_queue {
        unsigned short data_size;
        unsigned char  desc_size;
        unsigned char  winfo_size;
+       unsigned short priv_size;
 
        unsigned short usb_endpoint;
        unsigned short usb_maxpacket;
 };
 
-/**
- * struct data_queue_desc: Data queue description
- *
- * The information in this structure is used by drivers
- * to inform rt2x00lib about the creation of the data queue.
- *
- * @entry_num: Maximum number of entries for a queue.
- * @data_size: Maximum data size for the frames in this queue.
- * @desc_size: Hardware descriptor size for the data in this queue.
- * @priv_size: Size of per-queue_entry private data.
- */
-struct data_queue_desc {
-       unsigned short entry_num;
-       unsigned short data_size;
-       unsigned char  desc_size;
-       unsigned char  winfo_size;
-       unsigned short priv_size;
-};
-
 /**
  * queue_end - Return pointer to the last queue (HELPER MACRO).
  * @__dev: Pointer to &struct rt2x00_dev
index 7e1759b..53754bc 100644 (file)
@@ -3025,26 +3025,40 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
        .config                 = rt61pci_config,
 };
 
-static const struct data_queue_desc rt61pci_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+static void rt61pci_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt61pci_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
 
-static const struct data_queue_desc rt61pci_queue_bcn = {
-       .entry_num              = 4,
-       .data_size              = 0, /* No DMA required for beacons */
-       .desc_size              = TXINFO_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_mmio),
-};
+       case QID_BEACON:
+               queue->limit = 4;
+               queue->data_size = 0; /* No DMA required for beacons */
+               queue->desc_size = TXINFO_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_mmio);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt61pci_ops = {
        .name                   = KBUILD_MODNAME,
@@ -3052,10 +3066,7 @@ static const struct rt2x00_ops rt61pci_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = 0,
-       .rx                     = &rt61pci_queue_rx,
-       .tx                     = &rt61pci_queue_tx,
-       .bcn                    = &rt61pci_queue_bcn,
+       .queue_init             = rt61pci_queue_init,
        .lib                    = &rt61pci_rt2x00_ops,
        .hw                     = &rt61pci_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 377e09b..1616ed4 100644 (file)
@@ -2359,26 +2359,40 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
        .config                 = rt73usb_config,
 };
 
-static const struct data_queue_desc rt73usb_queue_rx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = RXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+static void rt73usb_queue_init(struct data_queue *queue)
+{
+       switch (queue->qid) {
+       case QID_RX:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = RXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt73usb_queue_tx = {
-       .entry_num              = 32,
-       .data_size              = DATA_FRAME_SIZE,
-       .desc_size              = TXD_DESC_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               queue->limit = 32;
+               queue->data_size = DATA_FRAME_SIZE;
+               queue->desc_size = TXD_DESC_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
 
-static const struct data_queue_desc rt73usb_queue_bcn = {
-       .entry_num              = 4,
-       .data_size              = MGMT_FRAME_SIZE,
-       .desc_size              = TXINFO_SIZE,
-       .priv_size              = sizeof(struct queue_entry_priv_usb),
-};
+       case QID_BEACON:
+               queue->limit = 4;
+               queue->data_size = MGMT_FRAME_SIZE;
+               queue->desc_size = TXINFO_SIZE;
+               queue->priv_size = sizeof(struct queue_entry_priv_usb);
+               break;
+
+       case QID_ATIM:
+               /* fallthrough */
+       default:
+               BUG();
+               break;
+       }
+}
 
 static const struct rt2x00_ops rt73usb_ops = {
        .name                   = KBUILD_MODNAME,
@@ -2386,10 +2400,7 @@ static const struct rt2x00_ops rt73usb_ops = {
        .eeprom_size            = EEPROM_SIZE,
        .rf_size                = RF_SIZE,
        .tx_queues              = NUM_TX_QUEUES,
-       .extra_tx_headroom      = TXD_DESC_SIZE,
-       .rx                     = &rt73usb_queue_rx,
-       .tx                     = &rt73usb_queue_tx,
-       .bcn                    = &rt73usb_queue_bcn,
+       .queue_init             = rt73usb_queue_init,
        .lib                    = &rt73usb_rt2x00_ops,
        .hw                     = &rt73usb_mac80211_ops,
 #ifdef CONFIG_RT2X00_LIB_DEBUGFS
index 953f1a0..2119313 100644 (file)
@@ -104,7 +104,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
                        tx_agc[RF90_PATH_A] = 0x10101010;
                        tx_agc[RF90_PATH_B] = 0x10101010;
                } else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
-                          TXHIGHPWRLEVEL_LEVEL1) {
+                          TXHIGHPWRLEVEL_LEVEL2) {
                        tx_agc[RF90_PATH_A] = 0x00000000;
                        tx_agc[RF90_PATH_B] = 0x00000000;
                } else{
index 826f085..2bd5985 100644 (file)
@@ -359,6 +359,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = {
        {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
        {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/
        {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/
+       {RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/
        {RTL_USB_DEVICE(0x7392, 0x7822, rtl92cu_hal_cfg)}, /*Edimax -Edimax*/
        {}
 };
index 4c67c2f..c7dc6fe 100644 (file)
@@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl)
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
-       /*
-        * Set WSPI_INIT_COMMAND
+       /* Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
        cmd[2] = 0xff;
@@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi)
        wl->if_ops = &wl1251_spi_ops;
 
        /* This is the only SPI value that we need to set here, the rest
-        * comes from the board-peripherals file */
+        * comes from the board-peripherals file
+        */
        spi->bits_per_word = 32;
 
        ret = spi_setup(spi);
@@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = {
        .remove         = wl1251_spi_remove,
 };
 
-static int __init wl1251_spi_init(void)
-{
-       int ret;
-
-       ret = spi_register_driver(&wl1251_spi_driver);
-       if (ret < 0) {
-               wl1251_error("failed to register spi driver: %d", ret);
-               goto out;
-       }
-
-out:
-       return ret;
-}
-
-static void __exit wl1251_spi_exit(void)
-{
-       spi_unregister_driver(&wl1251_spi_driver);
-
-       wl1251_notice("unloaded");
-}
-
-module_init(wl1251_spi_init);
-module_exit(wl1251_spi_exit);
+module_spi_driver(wl1251_spi_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
index 9fa692d..7aa0eb8 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/ip.h>
 #include <linux/firmware.h>
+#include <linux/etherdevice.h>
 
 #include "../wlcore/wlcore.h"
 #include "../wlcore/debug.h"
@@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
                .mem3 = { .start = 0x00000000, .size  = 0x00000000 },
        },
        [PART_PHY_INIT] = {
-               .mem  = { .start = 0x80926000,
-                         .size = sizeof(struct wl18xx_mac_and_phy_params) },
+               .mem  = { .start = WL18XX_PHY_INIT_MEM_ADDR,
+                         .size  = WL18XX_PHY_INIT_MEM_SIZE },
                .reg  = { .start = 0x00000000, .size = 0x00000000 },
                .mem2 = { .start = 0x00000000, .size = 0x00000000 },
                .mem3 = { .start = 0x00000000, .size = 0x00000000 },
@@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
        u32 tmp;
        int ret;
 
+       BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) >
+               WL18XX_PHY_INIT_MEM_SIZE);
+
        ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
        if (ret < 0)
                goto out;
@@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
        wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
 
        ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp);
+       if (ret < 0)
+               goto out;
+
+       /*
+        * Workaround for FDSP code RAM corruption (needed for PG2.1
+        * and newer; for older chips it's a NOP).  Change FDSP clock
+        * settings so that it's muxed to the ATGP clock instead of
+        * its own clock.
+        */
+
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
+       if (ret < 0)
+               goto out;
+
+       /* disable FDSP clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CLK_120_DISABLE);
+       if (ret < 0)
+               goto out;
+
+       /* set ATPG clock toward FDSP Code RAM rather than its own clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CODERAM_FUNC_CLK_SEL);
+       if (ret < 0)
+               goto out;
+
+       /* re-enable FDSP clock */
+       ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+                            MEM_FDSP_CLK_120_ENABLE);
 
 out:
        return ret;
@@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl)
                ((mac1 & 0xff000000) >> 24);
        wl->fuse_nic_addr = (mac1 & 0xffffff);
 
+       if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) {
+               u8 mac[ETH_ALEN];
+
+               eth_random_addr(mac);
+
+               wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2];
+               wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5];
+               wl1271_warning("MAC address from fuse not available, using random locally administered addresses.");
+       }
+
        ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
 
 out:
index 6306e04..05dd8ba 100644 (file)
@@ -38,6 +38,9 @@
 #define WL18XX_REG_BOOT_PART_SIZE  0x00014578
 
 #define WL18XX_PHY_INIT_MEM_ADDR   0x80926000
+#define WL18XX_PHY_END_MEM_ADDR           0x8093CA44
+#define WL18XX_PHY_INIT_MEM_SIZE \
+       (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR)
 
 #define WL18XX_SDIO_WSPI_BASE          (WL18XX_REGISTERS_BASE)
 #define WL18XX_REG_CONFIG_BASE         (WL18XX_REGISTERS_BASE + 0x02000)
@@ -217,4 +220,16 @@ static const char * const rdl_names[] = {
        [RDL_4_SP]      = "1897 MIMO",
 };
 
+/* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */
+#define WL18XX_PHY_FPGA_SPARE_1                0x8093CA40
+
+/* command to disable FDSP clock */
+#define MEM_FDSP_CLK_120_DISABLE        0x80000000
+
+/* command to set ATPG clock toward FDSP Code RAM rather than its own clock */
+#define MEM_FDSP_CODERAM_FUNC_CLK_SEL  0xC0000000
+
+/* command to re-enable FDSP clock */
+#define MEM_FDSP_CLK_120_ENABLE                0x40000000
+
 #endif /* __REG_H__ */
index b21398f..4f23931 100644 (file)
@@ -1,5 +1,5 @@
 wlcore-objs            = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
-                         boot.o init.o debugfs.o scan.o
+                         boot.o init.o debugfs.o scan.o sysfs.o
 
 wlcore_spi-objs        = spi.o
 wlcore_sdio-objs       = sdio.o
index 953111a..b8db55c 100644 (file)
@@ -1,10 +1,9 @@
 
 /*
- * This file is part of wl1271
+ * This file is part of wlcore
  *
  * Copyright (C) 2008-2010 Nokia Corporation
- *
- * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ * Copyright (C) 2011-2013 Texas Instruments Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 
 #include <linux/module.h>
 #include <linux/firmware.h>
-#include <linux/delay.h>
-#include <linux/spi/spi.h>
-#include <linux/crc32.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <linux/wl12xx.h>
-#include <linux/sched.h>
 #include <linux/interrupt.h>
 
 #include "wlcore.h"
 #include "debug.h"
 #include "wl12xx_80211.h"
 #include "io.h"
-#include "event.h"
 #include "tx.h"
-#include "rx.h"
 #include "ps.h"
 #include "init.h"
 #include "debugfs.h"
-#include "cmd.h"
-#include "boot.h"
 #include "testmode.h"
 #include "scan.h"
 #include "hw_ops.h"
-
-#define WL1271_BOOT_RETRIES 3
+#include "sysfs.h"
 
 #define WL1271_BOOT_RETRIES 3
 
@@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
 static void wlcore_op_stop_locked(struct wl1271 *wl);
 static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 
-static int wl12xx_set_authorized(struct wl1271 *wl,
-                                struct wl12xx_vif *wlvif)
+static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
 
@@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+       wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                                  sizeof(*wl->fw_status_2) +
                                  wl->fw_status_priv_len, GFP_KERNEL);
        if (!wl->fw_status_1)
@@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl)
                                (((u8 *) wl->fw_status_1) +
                                WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
 
-       wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
+       wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
        if (!wl->tx_res_if) {
                kfree(wl->fw_status_1);
                return -ENOMEM;
@@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl,
        return 0;
 }
 
-static void wl1271_configure_resume(struct wl1271 *wl,
-                                   struct wl12xx_vif *wlvif)
+static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret = 0;
        bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
@@ -2603,6 +2589,7 @@ unlock:
        cancel_work_sync(&wlvif->rx_streaming_enable_work);
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
+       cancel_delayed_work_sync(&wlvif->channel_switch_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                if (ret < 0)
                        return ret;
 
-               /* the default WEP key needs to be configured at least once */
-               if (key_type == KEY_WEP) {
-                       ret = wl12xx_cmd_set_default_wep_key(wl,
-                                                       wlvif->default_key,
-                                                       wlvif->sta.hlid);
-                       if (ret < 0)
-                               return ret;
-               }
        }
 
        return 0;
@@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(wlcore_set_key);
 
+static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif,
+                                         int key_idx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
+                    key_idx);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EAGAIN;
+               goto out_unlock;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
+       wlvif->default_key = key_idx;
+
+       /* the default WEP key needs to be configured at least once */
+       if (wlvif->encryption_type == KEY_WEP) {
+               ret = wl12xx_cmd_set_default_wep_key(wl,
+                               key_idx,
+                               wlvif->sta.hlid);
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+
+out_unlock:
+       mutex_unlock(&wl->mutex);
+}
+
 void wlcore_regdomain_config(struct wl1271 *wl)
 {
        int ret;
@@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
        struct ieee80211_hdr *hdr;
        u32 min_rate;
        int ret;
-       int ieoffset = offsetof(struct ieee80211_mgmt,
-                               u.beacon.variable);
+       int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
        struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
        u16 tmpl_id;
 
@@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
        }
 
        /* Handle new association with HT. Do this after join. */
-       if (sta_exists &&
-           (changed & BSS_CHANGED_HT)) {
+       if (sta_exists) {
                bool enabled =
                        bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
 
@@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .ampdu_action = wl1271_op_ampdu_action,
        .tx_frames_pending = wl1271_tx_frames_pending,
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
+       .set_default_unicast_key = wl1271_op_set_default_key_idx,
        .channel_switch = wl12xx_op_channel_switch,
        .flush = wlcore_op_flush,
        .remain_on_channel = wlcore_op_remain_on_channel,
@@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
        return idx;
 }
 
-static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
-                                              struct device_attribute *attr,
-                                              char *buf)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-
-       len = PAGE_SIZE;
-
-       mutex_lock(&wl->mutex);
-       len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
-                      wl->sg_enabled);
-       mutex_unlock(&wl->mutex);
-
-       return len;
-
-}
-
-static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
-                                               struct device_attribute *attr,
-                                               const char *buf, size_t count)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       unsigned long res;
-       int ret;
-
-       ret = kstrtoul(buf, 10, &res);
-       if (ret < 0) {
-               wl1271_warning("incorrect value written to bt_coex_mode");
-               return count;
-       }
-
-       mutex_lock(&wl->mutex);
-
-       res = !!res;
-
-       if (res == wl->sg_enabled)
-               goto out;
-
-       wl->sg_enabled = res;
-
-       if (unlikely(wl->state != WLCORE_STATE_ON))
-               goto out;
-
-       ret = wl1271_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out;
-
-       wl1271_acx_sg_enable(wl, wl->sg_enabled);
-       wl1271_ps_elp_sleep(wl);
-
- out:
-       mutex_unlock(&wl->mutex);
-       return count;
-}
-
-static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
-                  wl1271_sysfs_show_bt_coex_state,
-                  wl1271_sysfs_store_bt_coex_state);
-
-static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
-                                          struct device_attribute *attr,
-                                          char *buf)
-{
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-
-       len = PAGE_SIZE;
-
-       mutex_lock(&wl->mutex);
-       if (wl->hw_pg_ver >= 0)
-               len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
-       else
-               len = snprintf(buf, len, "n/a\n");
-       mutex_unlock(&wl->mutex);
-
-       return len;
-}
-
-static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
-                  wl1271_sysfs_show_hw_pg_ver, NULL);
-
-static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
-                                      struct bin_attribute *bin_attr,
-                                      char *buffer, loff_t pos, size_t count)
-{
-       struct device *dev = container_of(kobj, struct device, kobj);
-       struct wl1271 *wl = dev_get_drvdata(dev);
-       ssize_t len;
-       int ret;
-
-       ret = mutex_lock_interruptible(&wl->mutex);
-       if (ret < 0)
-               return -ERESTARTSYS;
-
-       /* Let only one thread read the log at a time, blocking others */
-       while (wl->fwlog_size == 0) {
-               DEFINE_WAIT(wait);
-
-               prepare_to_wait_exclusive(&wl->fwlog_waitq,
-                                         &wait,
-                                         TASK_INTERRUPTIBLE);
-
-               if (wl->fwlog_size != 0) {
-                       finish_wait(&wl->fwlog_waitq, &wait);
-                       break;
-               }
-
-               mutex_unlock(&wl->mutex);
-
-               schedule();
-               finish_wait(&wl->fwlog_waitq, &wait);
-
-               if (signal_pending(current))
-                       return -ERESTARTSYS;
-
-               ret = mutex_lock_interruptible(&wl->mutex);
-               if (ret < 0)
-                       return -ERESTARTSYS;
-       }
-
-       /* Check if the fwlog is still valid */
-       if (wl->fwlog_size < 0) {
-               mutex_unlock(&wl->mutex);
-               return 0;
-       }
-
-       /* Seeking is not supported - old logs are not kept. Disregard pos. */
-       len = min(count, (size_t)wl->fwlog_size);
-       wl->fwlog_size -= len;
-       memcpy(buffer, wl->fwlog, len);
-
-       /* Make room for new messages */
-       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
-
-       mutex_unlock(&wl->mutex);
-
-       return len;
-}
-
-static struct bin_attribute fwlog_attr = {
-       .attr = {.name = "fwlog", .mode = S_IRUSR},
-       .read = wl1271_sysfs_read_fwlog,
-};
-
 static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
 {
        int i;
@@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        return 0;
 }
 
-#define WL1271_DEFAULT_CHANNEL 0
-
 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
                                     u32 mbox_size)
 {
@@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
                goto err_hw;
        }
 
-       wl->channel = WL1271_DEFAULT_CHANNEL;
+       wl->channel = 0;
        wl->rx_counter = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
        wl->band = IEEE80211_BAND_2GHZ;
@@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        wake_up_interruptible_all(&wl->fwlog_waitq);
        mutex_unlock(&wl->mutex);
 
-       device_remove_bin_file(wl->dev, &fwlog_attr);
-
-       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+       wlcore_sysfs_free(wl);
 
-       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
        kfree(wl->buffer_32);
        kfree(wl->mbox);
        free_page((unsigned long)wl->fwlog);
@@ -6018,6 +5886,15 @@ int wlcore_free_hw(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wlcore_free_hw);
 
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support wlcore_wowlan_support = {
+       .flags = WIPHY_WOWLAN_ANY,
+       .n_patterns = WL1271_MAX_RX_FILTERS,
+       .pattern_min_len = 1,
+       .pattern_max_len = WL1271_RX_FILTER_MAX_PATTERN_SIZE,
+};
+#endif
+
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
@@ -6071,14 +5948,8 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        if (!ret) {
                wl->irq_wake_enabled = true;
                device_init_wakeup(wl->dev, 1);
-               if (pdata->pwr_in_suspend) {
-                       wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
-                       wl->hw->wiphy->wowlan.n_patterns =
-                               WL1271_MAX_RX_FILTERS;
-                       wl->hw->wiphy->wowlan.pattern_min_len = 1;
-                       wl->hw->wiphy->wowlan.pattern_max_len =
-                               WL1271_RX_FILTER_MAX_PATTERN_SIZE;
-               }
+               if (pdata->pwr_in_suspend)
+                       wl->hw->wiphy->wowlan = &wlcore_wowlan_support;
        }
 #endif
        disable_irq(wl->irq);
@@ -6101,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        if (ret)
                goto out_irq;
 
-       /* Create sysfs file to control bt coex state */
-       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file bt_coex_state");
+       ret = wlcore_sysfs_init(wl);
+       if (ret)
                goto out_unreg;
-       }
-
-       /* Create sysfs file to get HW PG version */
-       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file hw_pg_ver");
-               goto out_bt_coex_state;
-       }
-
-       /* Create sysfs file for the FW log */
-       ret = device_create_bin_file(wl->dev, &fwlog_attr);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file fwlog");
-               goto out_hw_pg_ver;
-       }
 
        wl->initialized = true;
        goto out;
 
-out_hw_pg_ver:
-       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
-
-out_bt_coex_state:
-       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
-
 out_unreg:
        wl1271_unregister_hw(wl);
 
index 9654577..98066d4 100644 (file)
@@ -110,7 +110,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
        DECLARE_COMPLETION_ONSTACK(compl);
        unsigned long flags;
        int ret;
-       u32 start_time = jiffies;
+       unsigned long start_time = jiffies;
        bool pending = false;
 
        /*
index e264478..1b0cd98 100644 (file)
@@ -434,19 +434,7 @@ static struct spi_driver wl1271_spi_driver = {
        .remove         = wl1271_remove,
 };
 
-static int __init wl1271_init(void)
-{
-       return spi_register_driver(&wl1271_spi_driver);
-}
-
-static void __exit wl1271_exit(void)
-{
-       spi_unregister_driver(&wl1271_spi_driver);
-}
-
-module_init(wl1271_init);
-module_exit(wl1271_exit);
-
+module_spi_driver(wl1271_spi_driver);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
new file mode 100644 (file)
index 0000000..8e58349
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "wlcore.h"
+#include "debug.h"
+#include "ps.h"
+#include "sysfs.h"
+
+static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
+                                              struct device_attribute *attr,
+                                              char *buf)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+
+       len = PAGE_SIZE;
+
+       mutex_lock(&wl->mutex);
+       len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
+                      wl->sg_enabled);
+       mutex_unlock(&wl->mutex);
+
+       return len;
+
+}
+
+static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       unsigned long res;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &res);
+       if (ret < 0) {
+               wl1271_warning("incorrect value written to bt_coex_mode");
+               return count;
+       }
+
+       mutex_lock(&wl->mutex);
+
+       res = !!res;
+
+       if (res == wl->sg_enabled)
+               goto out;
+
+       wl->sg_enabled = res;
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl1271_acx_sg_enable(wl, wl->sg_enabled);
+       wl1271_ps_elp_sleep(wl);
+
+ out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
+                  wl1271_sysfs_show_bt_coex_state,
+                  wl1271_sysfs_store_bt_coex_state);
+
+static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+
+       len = PAGE_SIZE;
+
+       mutex_lock(&wl->mutex);
+       if (wl->hw_pg_ver >= 0)
+               len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
+       else
+               len = snprintf(buf, len, "n/a\n");
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
+                  wl1271_sysfs_show_hw_pg_ver, NULL);
+
+static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
+                                      struct bin_attribute *bin_attr,
+                                      char *buffer, loff_t pos, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct wl1271 *wl = dev_get_drvdata(dev);
+       ssize_t len;
+       int ret;
+
+       ret = mutex_lock_interruptible(&wl->mutex);
+       if (ret < 0)
+               return -ERESTARTSYS;
+
+       /* Let only one thread read the log at a time, blocking others */
+       while (wl->fwlog_size == 0) {
+               DEFINE_WAIT(wait);
+
+               prepare_to_wait_exclusive(&wl->fwlog_waitq,
+                                         &wait,
+                                         TASK_INTERRUPTIBLE);
+
+               if (wl->fwlog_size != 0) {
+                       finish_wait(&wl->fwlog_waitq, &wait);
+                       break;
+               }
+
+               mutex_unlock(&wl->mutex);
+
+               schedule();
+               finish_wait(&wl->fwlog_waitq, &wait);
+
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+
+               ret = mutex_lock_interruptible(&wl->mutex);
+               if (ret < 0)
+                       return -ERESTARTSYS;
+       }
+
+       /* Check if the fwlog is still valid */
+       if (wl->fwlog_size < 0) {
+               mutex_unlock(&wl->mutex);
+               return 0;
+       }
+
+       /* Seeking is not supported - old logs are not kept. Disregard pos. */
+       len = min(count, (size_t)wl->fwlog_size);
+       wl->fwlog_size -= len;
+       memcpy(buffer, wl->fwlog, len);
+
+       /* Make room for new messages */
+       memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
+
+       mutex_unlock(&wl->mutex);
+
+       return len;
+}
+
+static struct bin_attribute fwlog_attr = {
+       .attr = {.name = "fwlog", .mode = S_IRUSR},
+       .read = wl1271_sysfs_read_fwlog,
+};
+
+int wlcore_sysfs_init(struct wl1271 *wl)
+{
+       int ret;
+
+       /* Create sysfs file to control bt coex state */
+       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file bt_coex_state");
+               goto out;
+       }
+
+       /* Create sysfs file to get HW PG version */
+       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file hw_pg_ver");
+               goto out_bt_coex_state;
+       }
+
+       /* Create sysfs file for the FW log */
+       ret = device_create_bin_file(wl->dev, &fwlog_attr);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file fwlog");
+               goto out_hw_pg_ver;
+       }
+
+       goto out;
+
+out_hw_pg_ver:
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+out_bt_coex_state:
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+
+out:
+       return ret;
+}
+
+void wlcore_sysfs_free(struct wl1271 *wl)
+{
+       device_remove_bin_file(wl->dev, &fwlog_attr);
+
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+}
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.h b/drivers/net/wireless/ti/wlcore/sysfs.h
new file mode 100644 (file)
index 0000000..c148892
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __SYSFS_H__
+#define __SYSFS_H__
+
+int wlcore_sysfs_init(struct wl1271 *wl);
+void wlcore_sysfs_free(struct wl1271 *wl);
+
+#endif
index 004d02e..7e93fe6 100644 (file)
@@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
                         (cipher == WLAN_CIPHER_SUITE_WEP104);
 
-               if (unlikely(is_wep && wlvif->default_key != idx)) {
+               if (WARN_ON(is_wep && wlvif->default_key != idx)) {
                        ret = wl1271_set_default_wep_key(wl, wlvif, idx);
                        if (ret < 0)
                                return ret;
index a0b50ad..130bcb2 100644 (file)
@@ -1968,8 +1968,8 @@ static void __exit netback_fini(void)
                del_timer_sync(&netbk->net_timer);
                kthread_stop(netbk->task);
                for (j = 0; j < MAX_PENDING_REQS; j++) {
-                       if (netbk->mmap_pages[i])
-                               __free_page(netbk->mmap_pages[i]);
+                       if (netbk->mmap_pages[j])
+                               __free_page(netbk->mmap_pages[j]);
                }
        }
 
index 74a852e..b0b64cc 100644 (file)
@@ -36,6 +36,16 @@ config NFC_MEI_PHY
 
          If unsure, say N.
 
+config NFC_SIM
+       tristate "NFC hardware simulator driver"
+       help
+         This driver declares two virtual NFC devices supporting NFC-DEP
+         protocol. An LLCP connection can be established between them and
+         all packets sent from one device is sent back to the other, acting as
+         loopback devices.
+
+         If unsure, say N.
+
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 
index aa6bd65..be7636a 100644 (file)
@@ -7,5 +7,6 @@ obj-$(CONFIG_NFC_MICROREAD)     += microread/
 obj-$(CONFIG_NFC_PN533)                += pn533.o
 obj-$(CONFIG_NFC_WILINK)       += nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)      += mei_phy.o
+obj-$(CONFIG_NFC_SIM)          += nfcsim.o
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index 1201bdb..606bf55 100644 (file)
@@ -30,7 +30,7 @@ struct mei_nfc_hdr {
        u16 req_id;
        u32 reserved;
        u16 data_size;
-} __attribute__((packed));
+} __packed;
 
 #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
 
@@ -60,8 +60,8 @@ int nfc_mei_phy_enable(void *phy_id)
 
        r = mei_cl_enable_device(phy->device);
        if (r < 0) {
-                pr_err("MEI_PHY: Could not enable device\n");
-                return r;
+               pr_err("MEI_PHY: Could not enable device\n");
+               return r;
        }
 
        r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy);
index 3420d83..cdb9f6d 100644 (file)
@@ -650,7 +650,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
 {
        struct microread_info *info;
        unsigned long quirks = 0;
-       u32 protocols, se;
+       u32 protocols;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -678,10 +678,8 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
-
        info->hdev = nfc_hci_allocate_device(&microread_hci_ops, &init_data,
-                                            quirks, protocols, se, llc_name,
+                                            quirks, protocols, llc_name,
                                             phy_headroom +
                                             MICROREAD_CMDS_HEADROOM,
                                             phy_tailroom +
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
new file mode 100644 (file)
index 0000000..c5c30fb
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * NFC hardware simulation driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nfc.h>
+
+#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \
+                                               "%s: " fmt, __func__, ## args)
+
+#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \
+                                               "%s: " fmt, __func__, ## args)
+
+#define NFCSIM_VERSION "0.1"
+
+#define NFCSIM_POLL_NONE       0
+#define NFCSIM_POLL_INITIATOR  1
+#define NFCSIM_POLL_TARGET     2
+#define NFCSIM_POLL_DUAL       (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
+
+struct nfcsim {
+       struct nfc_dev *nfc_dev;
+
+       struct mutex lock;
+
+       struct delayed_work recv_work;
+
+       struct sk_buff *clone_skb;
+
+       struct delayed_work poll_work;
+       u8 polling_mode;
+       u8 curr_polling_mode;
+
+       u8 shutting_down;
+
+       u8 up;
+
+       u8 initiator;
+
+       data_exchange_cb_t cb;
+       void *cb_context;
+
+       struct nfcsim *peer_dev;
+};
+
+static struct nfcsim *dev0;
+static struct nfcsim *dev1;
+
+struct workqueue_struct *wq;
+
+static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
+{
+       DEV_DBG(dev, "shutdown=%d", shutdown);
+
+       mutex_lock(&dev->lock);
+
+       dev->polling_mode = NFCSIM_POLL_NONE;
+       dev->shutting_down = shutdown;
+       dev->cb = NULL;
+       dev_kfree_skb(dev->clone_skb);
+       dev->clone_skb = NULL;
+
+       mutex_unlock(&dev->lock);
+
+       cancel_delayed_work_sync(&dev->poll_work);
+       cancel_delayed_work_sync(&dev->recv_work);
+}
+
+static int nfcsim_target_found(struct nfcsim *dev)
+{
+       struct nfc_target nfc_tgt;
+
+       DEV_DBG(dev, "");
+
+       memset(&nfc_tgt, 0, sizeof(struct nfc_target));
+
+       nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+       nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
+
+       return 0;
+}
+
+static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       mutex_lock(&dev->lock);
+
+       dev->up = 1;
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       mutex_lock(&dev->lock);
+
+       dev->up = 0;
+
+       mutex_unlock(&dev->lock);
+
+       return 0;
+}
+
+static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
+                             struct nfc_target *target,
+                             u8 comm_mode, u8 *gb, size_t gb_len)
+{
+       int rc;
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       struct nfcsim *peer = dev->peer_dev;
+       u8 *remote_gb;
+       size_t remote_gb_len;
+
+       DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode);
+
+       mutex_lock(&peer->lock);
+
+       nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+                        NFC_COMM_ACTIVE, gb, gb_len);
+
+       remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
+       if (!remote_gb) {
+               DEV_ERR(peer, "Can't get remote general bytes");
+
+               mutex_unlock(&peer->lock);
+               return -EINVAL;
+       }
+
+       mutex_unlock(&peer->lock);
+
+       mutex_lock(&dev->lock);
+
+       rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
+       if (rc) {
+               DEV_ERR(dev, "Can't set remote general bytes");
+               mutex_unlock(&dev->lock);
+               return rc;
+       }
+
+       rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE,
+                               NFC_RF_INITIATOR);
+
+       mutex_unlock(&dev->lock);
+
+       return rc;
+}
+
+static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       nfcsim_cleanup_dev(dev, 0);
+
+       return 0;
+}
+
+static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
+                            u32 im_protocols, u32 tm_protocols)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->polling_mode != NFCSIM_POLL_NONE) {
+               DEV_ERR(dev, "Already in polling mode");
+               rc = -EBUSY;
+               goto exit;
+       }
+
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
+               dev->polling_mode |= NFCSIM_POLL_INITIATOR;
+
+       if (tm_protocols & NFC_PROTO_NFC_DEP_MASK)
+               dev->polling_mode |= NFCSIM_POLL_TARGET;
+
+       if (dev->polling_mode == NFCSIM_POLL_NONE) {
+               DEV_ERR(dev, "Unsupported polling mode");
+               rc = -EINVAL;
+               goto exit;
+       }
+
+       dev->initiator = 0;
+       dev->curr_polling_mode = NFCSIM_POLL_NONE;
+
+       queue_delayed_work(wq, &dev->poll_work, 0);
+
+       DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols,
+               tm_protocols);
+
+       rc = 0;
+exit:
+       mutex_unlock(&dev->lock);
+
+       return rc;
+}
+
+static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "Stop poll");
+
+       mutex_lock(&dev->lock);
+
+       dev->polling_mode = NFCSIM_POLL_NONE;
+
+       mutex_unlock(&dev->lock);
+
+       cancel_delayed_work_sync(&dev->poll_work);
+}
+
+static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
+                                 struct nfc_target *target, u32 protocol)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+
+       return -ENOTSUPP;
+}
+
+static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
+                                    struct nfc_target *target)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+
+       DEV_DBG(dev, "");
+}
+
+static void nfcsim_wq_recv(struct work_struct *work)
+{
+       struct nfcsim *dev = container_of(work, struct nfcsim,
+                                         recv_work.work);
+
+       mutex_lock(&dev->lock);
+
+       if (dev->shutting_down || !dev->up || !dev->clone_skb) {
+               dev_kfree_skb(dev->clone_skb);
+               goto exit;
+       }
+
+       if (dev->initiator) {
+               if (!dev->cb) {
+                       DEV_ERR(dev, "Null recv callback");
+                       dev_kfree_skb(dev->clone_skb);
+                       goto exit;
+               }
+
+               dev->cb(dev->cb_context, dev->clone_skb, 0);
+               dev->cb = NULL;
+       } else {
+               nfc_tm_data_received(dev->nfc_dev, dev->clone_skb);
+       }
+
+exit:
+       dev->clone_skb = NULL;
+
+       mutex_unlock(&dev->lock);
+}
+
+static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                    struct sk_buff *skb, data_exchange_cb_t cb,
+                    void *cb_context)
+{
+       struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
+       struct nfcsim *peer = dev->peer_dev;
+       int err;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->shutting_down || !dev->up) {
+               mutex_unlock(&dev->lock);
+               err = -ENODEV;
+               goto exit;
+       }
+
+       dev->cb = cb;
+       dev->cb_context = cb_context;
+
+       mutex_unlock(&dev->lock);
+
+       mutex_lock(&peer->lock);
+
+       peer->clone_skb = skb_clone(skb, GFP_KERNEL);
+
+       if (!peer->clone_skb) {
+               DEV_ERR(dev, "skb_clone failed");
+               mutex_unlock(&peer->lock);
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* This simulates an arbitrary transmission delay between the 2 devices.
+        * If packet transmission occurs immediately between them, we have a
+        * non-stop flow of several tens of thousands SYMM packets per second
+        * and a burning cpu.
+        *
+        * TODO: Add support for a sysfs entry to control this delay.
+        */
+       queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5));
+
+       mutex_unlock(&peer->lock);
+
+       err = 0;
+exit:
+       dev_kfree_skb(skb);
+
+       return err;
+}
+
+static int nfcsim_im_transceive(struct nfc_dev *nfc_dev,
+                               struct nfc_target *target, struct sk_buff *skb,
+                               data_exchange_cb_t cb, void *cb_context)
+{
+       return nfcsim_tx(nfc_dev, target, skb, cb, cb_context);
+}
+
+static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+       return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL);
+}
+
+static struct nfc_ops nfcsim_nfc_ops = {
+       .dev_up = nfcsim_dev_up,
+       .dev_down = nfcsim_dev_down,
+       .dep_link_up = nfcsim_dep_link_up,
+       .dep_link_down = nfcsim_dep_link_down,
+       .start_poll = nfcsim_start_poll,
+       .stop_poll = nfcsim_stop_poll,
+       .activate_target = nfcsim_activate_target,
+       .deactivate_target = nfcsim_deactivate_target,
+       .im_transceive = nfcsim_im_transceive,
+       .tm_send = nfcsim_tm_send,
+};
+
+static void nfcsim_set_polling_mode(struct nfcsim *dev)
+{
+       if (dev->polling_mode == NFCSIM_POLL_NONE) {
+               dev->curr_polling_mode = NFCSIM_POLL_NONE;
+               return;
+       }
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+               if (dev->polling_mode & NFCSIM_POLL_INITIATOR)
+                       dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+               else
+                       dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+
+               return;
+       }
+
+       if (dev->polling_mode == NFCSIM_POLL_DUAL) {
+               if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+                       dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
+               else
+                       dev->curr_polling_mode = NFCSIM_POLL_TARGET;
+       }
+}
+
+static void nfcsim_wq_poll(struct work_struct *work)
+{
+       struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work);
+       struct nfcsim *peer = dev->peer_dev;
+
+       /* These work items run on an ordered workqueue and are therefore
+        * serialized. So we can take both mutexes without being dead locked.
+        */
+       mutex_lock(&dev->lock);
+       mutex_lock(&peer->lock);
+
+       nfcsim_set_polling_mode(dev);
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
+               DEV_DBG(dev, "Not polling");
+               goto unlock;
+       }
+
+       DEV_DBG(dev, "Polling as %s",
+               dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
+               "initiator" : "target");
+
+       if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
+               goto sched_work;
+
+       if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) {
+               peer->polling_mode = NFCSIM_POLL_NONE;
+               dev->polling_mode = NFCSIM_POLL_NONE;
+
+               dev->initiator = 1;
+
+               nfcsim_target_found(dev);
+
+               goto unlock;
+       }
+
+sched_work:
+       /* This defines the delay for an initiator to check if the other device
+        * is polling in target mode.
+        * If the device starts in dual mode polling, it switches between
+        * initiator and target at every round.
+        * Because the wq is ordered and only 1 work item is executed at a time,
+        * we'll always have one device polling as initiator and the other as
+        * target at some point, even if both are started in dual mode.
+        */
+       queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200));
+
+unlock:
+       mutex_unlock(&peer->lock);
+       mutex_unlock(&dev->lock);
+}
+
+static struct nfcsim *nfcsim_init_dev(void)
+{
+       struct nfcsim *dev;
+       int rc = -ENOMEM;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&dev->lock);
+
+       INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv);
+       INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll);
+
+       dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops,
+                                          NFC_PROTO_NFC_DEP_MASK,
+                                          0, 0);
+       if (!dev->nfc_dev)
+               goto error;
+
+       nfc_set_drvdata(dev->nfc_dev, dev);
+
+       rc = nfc_register_device(dev->nfc_dev);
+       if (rc)
+               goto free_nfc_dev;
+
+       return dev;
+
+free_nfc_dev:
+       nfc_free_device(dev->nfc_dev);
+
+error:
+       kfree(dev);
+
+       return ERR_PTR(rc);
+}
+
+static void nfcsim_free_device(struct nfcsim *dev)
+{
+       nfc_unregister_device(dev->nfc_dev);
+
+       nfc_free_device(dev->nfc_dev);
+
+       kfree(dev);
+}
+
+int __init nfcsim_init(void)
+{
+       int rc;
+
+       /* We need an ordered wq to ensure that poll_work items are executed
+        * one at a time.
+        */
+       wq = alloc_ordered_workqueue("nfcsim", 0);
+       if (!wq) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       dev0 = nfcsim_init_dev();
+       if (IS_ERR(dev0)) {
+               rc = PTR_ERR(dev0);
+               goto exit;
+       }
+
+       dev1 = nfcsim_init_dev();
+       if (IS_ERR(dev1)) {
+               kfree(dev0);
+
+               rc = PTR_ERR(dev1);
+               goto exit;
+       }
+
+       dev0->peer_dev = dev1;
+       dev1->peer_dev = dev0;
+
+       pr_debug("NFCsim " NFCSIM_VERSION " initialized\n");
+
+       rc = 0;
+exit:
+       if (rc)
+               pr_err("Failed to initialize nfcsim driver (%d)\n",
+                      rc);
+
+       return rc;
+}
+
+void __exit nfcsim_exit(void)
+{
+       nfcsim_cleanup_dev(dev0, 1);
+       nfcsim_cleanup_dev(dev1, 1);
+
+       nfcsim_free_device(dev0);
+       nfcsim_free_device(dev1);
+
+       destroy_workqueue(wq);
+}
+
+module_init(nfcsim_init);
+module_exit(nfcsim_exit);
+
+MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION);
+MODULE_VERSION(NFCSIM_VERSION);
+MODULE_LICENSE("GPL");
index 3b731ac..59f95d8 100644 (file)
@@ -109,7 +109,7 @@ enum {
        NFCWILINK_FW_DOWNLOAD,
 };
 
-static int nfcwilink_send(struct sk_buff *skb);
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb);
 
 static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
 {
@@ -156,8 +156,6 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
                return -ENOMEM;
        }
 
-       skb->dev = (void *)drv->ndev;
-
        cmd = (struct nci_vs_nfcc_info_cmd *)
                        skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
        cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
@@ -166,7 +164,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
 
        drv->nfcc_info.plen = 0;
 
-       rc = nfcwilink_send(skb);
+       rc = nfcwilink_send(drv->ndev, skb);
        if (rc)
                return rc;
 
@@ -232,11 +230,9 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
                return -ENOMEM;
        }
 
-       skb->dev = (void *)drv->ndev;
-
        memcpy(skb_put(skb, len), data, len);
 
-       rc = nfcwilink_send(skb);
+       rc = nfcwilink_send(drv->ndev, skb);
        if (rc)
                return rc;
 
@@ -371,10 +367,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
                return 0;
        }
 
-       skb->dev = (void *) drv->ndev;
-
        /* Forward skb to NCI core layer */
-       rc = nci_recv_frame(skb);
+       rc = nci_recv_frame(drv->ndev, skb);
        if (rc < 0) {
                nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc);
                return rc;
@@ -480,9 +474,8 @@ static int nfcwilink_close(struct nci_dev *ndev)
        return rc;
 }
 
-static int nfcwilink_send(struct sk_buff *skb)
+static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *)skb->dev;
        struct nfcwilink *drv = nci_get_drvdata(ndev);
        struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000};
        long len;
@@ -542,7 +535,6 @@ static int nfcwilink_probe(struct platform_device *pdev)
 
        drv->ndev = nci_allocate_device(&nfcwilink_ops,
                                        protocols,
-                                       NFC_SE_NONE,
                                        NFCWILINK_HDR_LEN,
                                        0);
        if (!drv->ndev) {
index 8f6f2ba..bfb4a4e 100644 (file)
@@ -258,7 +258,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
                                .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
-                               .tsn = 0,
+                               .tsn = 0x03,
                        },
                },
                .len = 7,
@@ -271,7 +271,7 @@ static const struct pn533_poll_modulations poll_mod[] = {
                                .opcode = PN533_FELICA_OPC_SENSF_REQ,
                                .sc = PN533_FELICA_SENSF_SC_ALL,
                                .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
-                               .tsn = 0,
+                               .tsn = 0x03,
                        },
                 },
                .len = 7,
@@ -1235,7 +1235,7 @@ static int pn533_target_found_type_a(struct nfc_target *nfc_tgt, u8 *tgt_data,
 struct pn533_target_felica {
        u8 pol_res;
        u8 opcode;
-       u8 nfcid2[8];
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
        u8 pad[8];
        /* optional */
        u8 syst_code[];
@@ -1275,6 +1275,9 @@ static int pn533_target_found_felica(struct nfc_target *nfc_tgt, u8 *tgt_data,
        memcpy(nfc_tgt->sensf_res, &tgt_felica->opcode, 9);
        nfc_tgt->sensf_res_len = 9;
 
+       memcpy(nfc_tgt->nfcid2, tgt_felica->nfcid2, NFC_NFCID2_MAXSIZE);
+       nfc_tgt->nfcid2_len = NFC_NFCID2_MAXSIZE;
+
        return 0;
 }
 
@@ -2084,6 +2087,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
        if (comm_mode == NFC_COMM_PASSIVE)
                skb_len += PASSIVE_DATA_LEN;
 
+       if (target && target->nfcid2_len)
+               skb_len += NFC_NFCID3_MAXSIZE;
+
        skb = pn533_alloc_skb(dev, skb_len);
        if (!skb)
                return -ENOMEM;
@@ -2100,6 +2106,12 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                *next |= 1;
        }
 
+       if (target && target->nfcid2_len) {
+               memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
+                      target->nfcid2_len);
+               *next |= 2;
+       }
+
        if (gb != NULL && gb_len > 0) {
                memcpy(skb_put(skb, gb_len), gb, gb_len);
                *next |= 4; /* We have some Gi */
@@ -2489,7 +2501,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb)
 
        nfc_dev_dbg(&urb->dev->dev, "%s", __func__);
 
-       print_hex_dump(KERN_ERR, "ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
+       print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1,
                       urb->transfer_buffer, urb->transfer_buffer_length,
                       false);
 
@@ -2520,7 +2532,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev)
        dev->out_urb->transfer_buffer = cmd;
        dev->out_urb->transfer_buffer_length = sizeof(cmd);
 
-       print_hex_dump(KERN_ERR, "ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
+       print_hex_dump_debug("ACR122 TX: ", DUMP_PREFIX_NONE, 16, 1,
                       cmd, sizeof(cmd), false);
 
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
@@ -2774,17 +2786,18 @@ static int pn533_probe(struct usb_interface *interface,
                goto destroy_wq;
 
        nfc_dev_info(&dev->interface->dev,
-                    "NXP PN533 firmware ver %d.%d now attached",
-                    fw_ver.ver, fw_ver.rev);
+                    "NXP PN5%02X firmware ver %d.%d now attached",
+                    fw_ver.ic, fw_ver.ver, fw_ver.rev);
 
 
        dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols,
-                                          NFC_SE_NONE,
                                           dev->ops->tx_header_len +
                                           PN533_CMD_DATAEXCH_HEAD_LEN,
                                           dev->ops->tx_tail_len);
-       if (!dev->nfc_dev)
+       if (!dev->nfc_dev) {
+               rc = -ENOMEM;
                goto destroy_wq;
+       }
 
        nfc_set_parent_dev(dev->nfc_dev, &interface->dev);
        nfc_set_drvdata(dev->nfc_dev, dev);
index 9c5f16e..0d17da7 100644 (file)
@@ -551,20 +551,25 @@ static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
                        return -EPROTO;
                }
 
-               r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
-                                    PN544_RF_READER_CMD_ACTIVATE_NEXT,
-                                    uid_skb->data, uid_skb->len, NULL);
-               kfree_skb(uid_skb);
-
-               r = nfc_hci_send_cmd(hdev,
+               /* Type F NFC-DEP IDm has prefix 0x01FE */
+               if ((uid_skb->data[0] == 0x01) && (uid_skb->data[1] == 0xfe)) {
+                       kfree_skb(uid_skb);
+                       r = nfc_hci_send_cmd(hdev,
                                        PN544_RF_READER_NFCIP1_INITIATOR_GATE,
                                        PN544_HCI_CMD_CONTINUE_ACTIVATION,
                                        NULL, 0, NULL);
-               if (r < 0)
-                       return r;
+                       if (r < 0)
+                               return r;
 
-               target->hci_reader_gate = PN544_RF_READER_NFCIP1_INITIATOR_GATE;
-               target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+                       target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
+                       target->hci_reader_gate =
+                               PN544_RF_READER_NFCIP1_INITIATOR_GATE;
+               } else {
+                       r = nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
+                                            PN544_RF_READER_CMD_ACTIVATE_NEXT,
+                                            uid_skb->data, uid_skb->len, NULL);
+                       kfree_skb(uid_skb);
+               }
        } else if (target->supported_protocols & NFC_PROTO_ISO14443_MASK) {
                /*
                 * TODO: maybe other ISO 14443 require some kind of continue
@@ -706,12 +711,9 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
                 return nfc_hci_send_cmd(hdev, NFC_HCI_RF_READER_A_GATE,
                                     PN544_RF_READER_CMD_ACTIVATE_NEXT,
                                     target->nfcid1, target->nfcid1_len, NULL);
-       } else if (target->supported_protocols & NFC_PROTO_JEWEL_MASK) {
-               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                       PN544_JEWEL_RAW_CMD, NULL, 0, NULL);
-       } else if (target->supported_protocols & NFC_PROTO_FELICA_MASK) {
-               return nfc_hci_send_cmd(hdev, PN544_RF_READER_F_GATE,
-                                       PN544_FELICA_RAW, NULL, 0, NULL);
+       } else if (target->supported_protocols & (NFC_PROTO_JEWEL_MASK |
+                                               NFC_PROTO_FELICA_MASK)) {
+               return -EOPNOTSUPP;
        } else if (target->supported_protocols & NFC_PROTO_NFC_DEP_MASK) {
                return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
                                        PN544_HCI_CMD_ATTREQUEST,
@@ -801,7 +803,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    struct nfc_hci_dev **hdev)
 {
        struct pn544_hci_info *info;
-       u32 protocols, se;
+       u32 protocols;
        struct nfc_hci_init_data init_data;
        int r;
 
@@ -834,10 +836,8 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
                    NFC_PROTO_ISO14443_B_MASK |
                    NFC_PROTO_NFC_DEP_MASK;
 
-       se = NFC_SE_UICC | NFC_SE_EMBEDDED;
-
        info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0,
-                                            protocols, se, llc_name,
+                                            protocols, llc_name,
                                             phy_headroom + PN544_CMDS_HEADROOM,
                                             phy_tailroom, phy_payload);
        if (!info->hdev) {
index 9ca3996..279ad50 100644 (file)
@@ -130,26 +130,6 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level)
 /**
  * some more debug stuff
  */
-#define IUCV_HEXDUMP16(importance,header,ptr) \
-PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
-                  "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
-                  *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
-                  *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
-                  *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
-                  *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
-                  *(((char*)ptr)+12),*(((char*)ptr)+13), \
-                  *(((char*)ptr)+14),*(((char*)ptr)+15)); \
-PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
-                  "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
-                  *(((char*)ptr)+16),*(((char*)ptr)+17), \
-                  *(((char*)ptr)+18),*(((char*)ptr)+19), \
-                  *(((char*)ptr)+20),*(((char*)ptr)+21), \
-                  *(((char*)ptr)+22),*(((char*)ptr)+23), \
-                  *(((char*)ptr)+24),*(((char*)ptr)+25), \
-                  *(((char*)ptr)+26),*(((char*)ptr)+27), \
-                  *(((char*)ptr)+28),*(((char*)ptr)+29), \
-                  *(((char*)ptr)+30),*(((char*)ptr)+31));
-
 #define PRINTK_HEADER " iucv: "       /* for debugging */
 
 /* dummy device to make sure netiucv_pm functions are called */
index c4f392d..41ef943 100644 (file)
@@ -738,7 +738,7 @@ struct qeth_rx {
        int qdio_err;
 };
 
-#define QETH_NAPI_WEIGHT 128
+#define QETH_NAPI_WEIGHT NAPI_POLL_WEIGHT
 
 struct qeth_card {
        struct list_head list;
index 6cd0fc1..e4ca704 100644 (file)
@@ -1282,8 +1282,10 @@ static void qeth_free_qdio_buffers(struct qeth_card *card)
 
        qeth_free_cq(card);
        cancel_delayed_work_sync(&card->buffer_reclaim_work);
-       for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
-               dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb);
+       for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) {
+               if (card->qdio.in_q->bufs[j].rx_skb)
+                       dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb);
+       }
        kfree(card->qdio.in_q);
        card->qdio.in_q = NULL;
        /* inbound buffer pool */
@@ -1729,14 +1731,14 @@ static void qeth_configure_blkt_default(struct qeth_card *card, char *prcd)
        QETH_DBF_TEXT(SETUP, 2, "cfgblkt");
 
        if (prcd[74] == 0xF0 && prcd[75] == 0xF0 &&
-           (prcd[76] == 0xF5 || prcd[76] == 0xF6)) {
-               card->info.blkt.time_total = 250;
-               card->info.blkt.inter_packet = 5;
-               card->info.blkt.inter_packet_jumbo = 15;
-       } else {
+           prcd[76] >= 0xF1 && prcd[76] <= 0xF4) {
                card->info.blkt.time_total = 0;
                card->info.blkt.inter_packet = 0;
                card->info.blkt.inter_packet_jumbo = 0;
+       } else {
+               card->info.blkt.time_total = 250;
+               card->info.blkt.inter_packet = 5;
+               card->info.blkt.inter_packet_jumbo = 15;
        }
 }
 
@@ -2198,11 +2200,11 @@ static inline int qeth_get_initial_mtu_for_card(struct qeth_card *card)
                case QETH_LINK_TYPE_LANE_TR:
                        return 2000;
                default:
-                       return 1492;
+                       return card->options.layer2 ? 1500 : 1492;
                }
        case QETH_CARD_TYPE_OSM:
        case QETH_CARD_TYPE_OSX:
-               return 1492;
+               return card->options.layer2 ? 1500 : 1492;
        default:
                return 1500;
        }
@@ -2275,9 +2277,10 @@ static int qeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply,
                card->info.max_mtu = mtu;
                card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE;
        } else {
-               card->info.initial_mtu = qeth_get_initial_mtu_for_card(card);
                card->info.max_mtu = *(__u16 *)QETH_ULP_ENABLE_RESP_MAX_MTU(
                        iob->data);
+               card->info.initial_mtu = min(card->info.max_mtu,
+                                       qeth_get_initial_mtu_for_card(card));
                card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
        }
 
index 552e8a2..448eae8 100644 (file)
@@ -906,7 +906,6 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
                        ISCSI_DBG_TCP(conn, "no more data avail. Consumed %d\n",
                                      consumed);
                        *status = ISCSI_TCP_SKB_DONE;
-                       skb_abort_seq_read(&seq);
                        goto skb_done;
                }
                BUG_ON(segment->copied >= segment->size);
index 720665c..e84cf04 100644 (file)
@@ -9,6 +9,19 @@
 
 #include "ssb_private.h"
 
+static struct resource ssb_sflash_resource = {
+       .name   = "ssb_sflash",
+       .start  = SSB_FLASH2,
+       .end    = 0,
+       .flags  = IORESOURCE_MEM | IORESOURCE_READONLY,
+};
+
+struct platform_device ssb_sflash_dev = {
+       .name           = "ssb_sflash",
+       .resource       = &ssb_sflash_resource,
+       .num_resources  = 1,
+};
+
 struct ssb_sflash_tbl_e {
        char *name;
        u32 id;
@@ -16,7 +29,7 @@ struct ssb_sflash_tbl_e {
        u16 numblocks;
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { "M25P20", 0x11, 0x10000, 4, },
        { "M25P40", 0x12, 0x10000, 8, },
 
@@ -27,7 +40,7 @@ static struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { 0 },
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { "SST25WF512", 1, 0x1000, 16, },
        { "SST25VF512", 0x48, 0x1000, 16, },
        { "SST25WF010", 2, 0x1000, 32, },
@@ -45,7 +58,7 @@ static struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { 0 },
 };
 
-static struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
+static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
        { "AT45DB011", 0xc, 256, 512, },
        { "AT45DB021", 0x14, 256, 1024, },
        { "AT45DB041", 0x1c, 256, 2048, },
@@ -73,7 +86,8 @@ static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
 /* Initialize serial flash access */
 int ssb_sflash_init(struct ssb_chipcommon *cc)
 {
-       struct ssb_sflash_tbl_e *e;
+       struct ssb_sflash *sflash = &cc->dev->bus->mipscore.sflash;
+       const struct ssb_sflash_tbl_e *e;
        u32 id, id2;
 
        switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
@@ -131,9 +145,21 @@ int ssb_sflash_init(struct ssb_chipcommon *cc)
                return -ENOTSUPP;
        }
 
+       sflash->window = SSB_FLASH2;
+       sflash->blocksize = e->blocksize;
+       sflash->numblocks = e->numblocks;
+       sflash->size = sflash->blocksize * sflash->numblocks;
+       sflash->present = true;
+
        pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n",
                e->name, e->blocksize, e->numblocks);
 
+       /* Prepare platform device, but don't register it yet. It's too early,
+        * malloc (required by device_private_init) is not available yet. */
+       ssb_sflash_dev.resource[0].end = ssb_sflash_dev.resource[0].start +
+                                        sflash->size;
+       ssb_sflash_dev.dev.platform_data = sflash;
+
        pr_err("Serial flash support is not implemented yet!\n");
 
        return -ENOTSUPP;
index 812775a..e55ddf7 100644 (file)
@@ -553,6 +553,14 @@ static int ssb_devices_register(struct ssb_bus *bus)
        }
 #endif
 
+#ifdef CONFIG_SSB_SFLASH
+       if (bus->mipscore.sflash.present) {
+               err = platform_device_register(&ssb_sflash_dev);
+               if (err)
+                       pr_err("Error registering serial flash\n");
+       }
+#endif
+
        return 0;
 error:
        /* Unwind the already registered devices. */
index 32ed1fa..69161bb 100644 (file)
@@ -38,7 +38,7 @@ static int ssb_pcihost_resume(struct pci_dev *dev)
        struct ssb_bus *ssb = pci_get_drvdata(dev);
        int err;
 
-       pci_set_power_state(dev, 0);
+       pci_set_power_state(dev, PCI_D0);
        err = pci_enable_device(dev);
        if (err)
                return err;
index 4671f17..eb507a5 100644 (file)
@@ -243,6 +243,10 @@ static inline int ssb_sflash_init(struct ssb_chipcommon *cc)
 extern struct platform_device ssb_pflash_dev;
 #endif
 
+#ifdef CONFIG_SSB_SFLASH
+extern struct platform_device ssb_sflash_dev;
+#endif
+
 #ifdef CONFIG_SSB_DRIVER_EXTIF
 extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks);
 extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
index 8c1c96c..79b876e 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/rcupdate.h>
 #include <linux/hrtimer.h>
 #include <linux/sched/rt.h>
+#include <net/ll_poll.h>
 
 #include <asm/uaccess.h>
 
@@ -384,9 +385,10 @@ get_max:
 #define POLLEX_SET (POLLPRI)
 
 static inline void wait_key_set(poll_table *wait, unsigned long in,
-                               unsigned long out, unsigned long bit)
+                               unsigned long out, unsigned long bit,
+                               unsigned int ll_flag)
 {
-       wait->_key = POLLEX_SET;
+       wait->_key = POLLEX_SET | ll_flag;
        if (in & bit)
                wait->_key |= POLLIN_SET;
        if (out & bit)
@@ -400,6 +402,8 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
        poll_table *wait;
        int retval, i, timed_out = 0;
        unsigned long slack = 0;
+       unsigned int ll_flag = POLL_LL;
+       u64 ll_time = ll_end_time();
 
        rcu_read_lock();
        retval = max_select_fd(n, fds);
@@ -422,6 +426,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
        retval = 0;
        for (;;) {
                unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
+               bool can_ll = false;
 
                inp = fds->in; outp = fds->out; exp = fds->ex;
                rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
@@ -449,7 +454,8 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                                        f_op = f.file->f_op;
                                        mask = DEFAULT_POLLMASK;
                                        if (f_op && f_op->poll) {
-                                               wait_key_set(wait, in, out, bit);
+                                               wait_key_set(wait, in, out,
+                                                            bit, ll_flag);
                                                mask = (*f_op->poll)(f.file, wait);
                                        }
                                        fdput(f);
@@ -468,6 +474,11 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                                                retval++;
                                                wait->_qproc = NULL;
                                        }
+                                       if (mask & POLL_LL)
+                                               can_ll = true;
+                                       /* got something, stop busy polling */
+                                       if (retval)
+                                               ll_flag = 0;
                                }
                        }
                        if (res_in)
@@ -486,6 +497,9 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
                        break;
                }
 
+               if (can_ll && can_poll_ll(ll_time))
+                       continue;
+
                /*
                 * If this is the first loop and we have a timeout
                 * given, then we convert to ktime_t and set the to
@@ -717,7 +731,8 @@ struct poll_list {
  * pwait poll_table will be used by the fd-provided poll handler for waiting,
  * if pwait->_qproc is non-NULL.
  */
-static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
+static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait,
+                                    bool *can_ll, unsigned int ll_flag)
 {
        unsigned int mask;
        int fd;
@@ -731,7 +746,10 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
                        mask = DEFAULT_POLLMASK;
                        if (f.file->f_op && f.file->f_op->poll) {
                                pwait->_key = pollfd->events|POLLERR|POLLHUP;
+                               pwait->_key |= ll_flag;
                                mask = f.file->f_op->poll(f.file, pwait);
+                               if (mask & POLL_LL)
+                                       *can_ll = true;
                        }
                        /* Mask out unneeded events. */
                        mask &= pollfd->events | POLLERR | POLLHUP;
@@ -750,6 +768,8 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
        ktime_t expire, *to = NULL;
        int timed_out = 0, count = 0;
        unsigned long slack = 0;
+       unsigned int ll_flag = POLL_LL;
+       u64 ll_time = ll_end_time();
 
        /* Optimise the no-wait case */
        if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
@@ -762,6 +782,7 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
 
        for (;;) {
                struct poll_list *walk;
+               bool can_ll = false;
 
                for (walk = list; walk != NULL; walk = walk->next) {
                        struct pollfd * pfd, * pfd_end;
@@ -776,9 +797,10 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
                                 * this. They'll get immediately deregistered
                                 * when we break out and return.
                                 */
-                               if (do_pollfd(pfd, pt)) {
+                               if (do_pollfd(pfd, pt, &can_ll, ll_flag)) {
                                        count++;
                                        pt->_qproc = NULL;
+                                       ll_flag = 0;
                                }
                        }
                }
@@ -795,6 +817,8 @@ static int do_poll(unsigned int nfds,  struct poll_list *list,
                if (count || timed_out)
                        break;
 
+               if (can_ll && can_poll_ll(ll_time))
+                       continue;
                /*
                 * If this is the first loop and we have a timeout
                 * given, then we convert to ktime_t and set the to
diff --git a/include/linux/can/platform/flexcan.h b/include/linux/can/platform/flexcan.h
deleted file mode 100644 (file)
index 72b713a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2010 Marc Kleine-Budde <kernel@pengutronix.de>
- *
- * This file is released under the GPLv2
- *
- */
-
-#ifndef __CAN_PLATFORM_FLEXCAN_H
-#define __CAN_PLATFORM_FLEXCAN_H
-
-/**
- * struct flexcan_platform_data - flex CAN controller platform data
- * @transceiver_enable:         - called to power on/off the transceiver
- *
- */
-struct flexcan_platform_data {
-       void (*transceiver_switch)(int enable);
-};
-
-#endif /* __CAN_PLATFORM_FLEXCAN_H */
index d826e5a..b0dc87a 100644 (file)
@@ -146,6 +146,7 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
 #define IEEE80211_MAX_RTS_THRESHOLD    2353
 #define IEEE80211_MAX_AID              2007
 #define IEEE80211_MAX_TIM_LEN          251
+#define IEEE80211_MAX_MESH_PEERINGS    63
 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
    6.2.1.1.2.
 
index f49a9f6..ddd33fd 100644 (file)
@@ -65,6 +65,7 @@ struct macvlan_dev {
 
        DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ);
 
+       netdev_features_t       set_features;
        enum macvlan_mode       mode;
        u16                     flags;
        int (*receive)(struct sk_buff *skb);
@@ -75,6 +76,7 @@ struct macvlan_dev {
        struct list_head        queue_list;
        int                     numvtaps;
        int                     numqueues;
+       netdev_features_t       tap_features;
        int                     minor;
 };
 
index bbca128..b4fa5e4 100644 (file)
@@ -323,6 +323,11 @@ static inline ktime_t ktime_add_us(const ktime_t kt, const u64 usec)
        return ktime_add_ns(kt, usec * 1000);
 }
 
+static inline ktime_t ktime_add_ms(const ktime_t kt, const u64 msec)
+{
+       return ktime_add_ns(kt, msec * NSEC_PER_MSEC);
+}
+
 static inline ktime_t ktime_sub_us(const ktime_t kt, const u64 usec)
 {
        return ktime_sub_ns(kt, usec * 1000);
@@ -366,7 +371,15 @@ extern void ktime_get_ts(struct timespec *ts);
 static inline ktime_t ns_to_ktime(u64 ns)
 {
        static const ktime_t ktime_zero = { .tv64 = 0 };
+
        return ktime_add_ns(ktime_zero, ns);
 }
 
+static inline ktime_t ms_to_ktime(u64 ms)
+{
+       static const ktime_t ktime_zero = { .tv64 = 0 };
+
+       return ktime_add_ms(ktime_zero, ms);
+}
+
 #endif
index f78b430..7a6c396 100644 (file)
@@ -85,6 +85,22 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
 void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
 int netlink_sendskb(struct sock *sk, struct sk_buff *skb);
 
+static inline struct sk_buff *
+netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
+{
+       struct sk_buff *nskb;
+
+       nskb = skb_clone(skb, gfp_mask);
+       if (!nskb)
+               return NULL;
+
+       /* This is a large skb, set destructor callback to release head */
+       if (is_vmalloc_addr(skb->head))
+               nskb->destructor = skb->destructor;
+
+       return nskb;
+}
+
 /*
  *     skb should fit one page. This choice is good for headerless malloc.
  *     But we should limit to 8K so that userspace does not have to
@@ -145,4 +161,14 @@ static inline int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
        return __netlink_dump_start(ssk, skb, nlh, control);
 }
 
+struct netlink_tap {
+       struct net_device *dev;
+       struct module *module;
+       struct list_head list;
+};
+
+extern int netlink_add_tap(struct netlink_tap *nt);
+extern int __netlink_remove_tap(struct netlink_tap *nt);
+extern int netlink_remove_tap(struct netlink_tap *nt);
+
 #endif /* __LINUX_NETLINK_H */
index a7393ad..6b06023 100644 (file)
@@ -2384,6 +2384,7 @@ extern void              skb_split(struct sk_buff *skb,
                                 struct sk_buff *skb1, const u32 len);
 extern int            skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
                                 int shiftlen);
+extern void           skb_scrub_packet(struct sk_buff *skb);
 
 extern struct sk_buff *skb_segment(struct sk_buff *skb,
                                   netdev_features_t features);
index afe79d4..6535e47 100644 (file)
@@ -20,6 +20,18 @@ struct ssb_pflash {
        u32 window_size;
 };
 
+#ifdef CONFIG_SSB_SFLASH
+struct ssb_sflash {
+       bool present;
+       u32 window;
+       u32 blocksize;
+       u16 numblocks;
+       u32 size;
+
+       void *priv;
+};
+#endif
+
 struct ssb_mipscore {
        struct ssb_device *dev;
 
@@ -27,6 +39,9 @@ struct ssb_mipscore {
        struct ssb_serial_port serial_ports[4];
 
        struct ssb_pflash pflash;
+#ifdef CONFIG_SSB_SFLASH
+       struct ssb_sflash sflash;
+#endif
 };
 
 extern void ssb_mipscore_init(struct ssb_mipscore *mcore);
index 21f7027..f68eaf5 100644 (file)
@@ -155,6 +155,7 @@ extern bool ipv6_chk_mcast_addr(struct net_device *dev,
                                const struct in6_addr *group,
                                const struct in6_addr *src_addr);
 
+extern void ipv6_mc_dad_complete(struct inet6_dev *idev);
 /*
  * identify MLD packets for MLD filter exceptions
  */
index 6dd1959..6a43c34 100644 (file)
@@ -1124,6 +1124,9 @@ struct bss_parameters {
  *     setting for new peer links.
  * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake
  *     after transmitting its beacon.
+ * @plink_timeout: If no tx activity is seen from a STA we've established
+ *     peering with for longer than this time (in seconds), then remove it
+ *     from the STA's list of peers.  Default is 30 minutes.
  */
 struct mesh_config {
        u16 dot11MeshRetryTimeout;
@@ -1153,6 +1156,7 @@ struct mesh_config {
        u16 dot11MeshHWMPconfirmationInterval;
        enum nl80211_mesh_power_mode power_mode;
        u16 dot11MeshAwakeWindowDuration;
+       u32 plink_timeout;
 };
 
 /**
@@ -1172,6 +1176,7 @@ struct mesh_config {
  * @dtim_period: DTIM period to use
  * @beacon_interval: beacon interval to use
  * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
+ * @basic_rates: basic rates to use when creating the mesh
  *
  * These parameters are fixed when the mesh is created.
  */
@@ -1191,6 +1196,7 @@ struct mesh_setup {
        u8 dtim_period;
        u16 beacon_interval;
        int mcast_rate[IEEE80211_NUM_BANDS];
+       u32 basic_rates;
 };
 
 /**
@@ -2654,7 +2660,7 @@ struct wiphy {
        u32 hw_version;
 
 #ifdef CONFIG_PM
-       struct wiphy_wowlan_support wowlan;
+       const struct wiphy_wowlan_support *wowlan;
        struct cfg80211_wowlan *wowlan_config;
 #endif
 
@@ -2853,7 +2859,7 @@ struct cfg80211_cached_keys;
  * @current_bss: (private) Used by the internal configuration code
  * @channel: (private) Used by the internal configuration code to track
  *     the user-set AP, monitor and WDS channel
- * @preset_chan: (private) Used by the internal configuration code to
+ * @preset_chandef: (private) Used by the internal configuration code to
  *     track the channel to be used for AP later
  * @bssid: (private) Used by the internal configuration code
  * @ssid: (private) Used by the internal configuration code
@@ -2875,6 +2881,15 @@ struct cfg80211_cached_keys;
  * @p2p_started: true if this is a P2P Device that has been started
  * @cac_started: true if DFS channel availability check has been started
  * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
+ * @ps: powersave mode is enabled
+ * @ps_timeout: dynamic powersave timeout
+ * @ap_unexpected_nlportid: (private) netlink port ID of application
+ *     registered for unexpected class 3 frames (AP mode)
+ * @conn: (private) cfg80211 software SME connection state machine data
+ * @connect_keys: (private) keys to set after connection is established
+ * @ibss_fixed: (private) IBSS is using fixed BSSID
+ * @event_list: (private) list for internal event processing
+ * @event_lock: (private) lock for event list
  */
 struct wireless_dev {
        struct wiphy *wiphy;
@@ -2898,11 +2913,6 @@ struct wireless_dev {
        /* currently used for IBSS and SME - might be rearranged later */
        u8 ssid[IEEE80211_MAX_SSID_LEN];
        u8 ssid_len, mesh_id_len, mesh_id_up_len;
-       enum {
-               CFG80211_SME_IDLE,
-               CFG80211_SME_CONNECTING,
-               CFG80211_SME_CONNECTED,
-       } sme_state;
        struct cfg80211_conn *conn;
        struct cfg80211_cached_keys *connect_keys;
 
@@ -3432,59 +3442,66 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
 
 /**
- * cfg80211_send_rx_auth - notification of processed authentication
+ * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
  * @dev: network device
  * @buf: authentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever an authentication has been processed in
- * station mode. The driver is required to call either this function or
- * cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth()
- * call. This function may sleep. The caller must hold the corresponding wdev's
- * mutex.
+ * This function is called whenever an authentication, disassociation or
+ * deauthentication frame has been received and processed in station mode.
+ * After being asked to authenticate via cfg80211_ops::auth() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ * While connected, the driver must calls this for received and processed
+ * disassociation and deauthentication frames. If the frame couldn't be used
+ * because it was unprotected, the driver must call the function
+ * cfg80211_rx_unprot_mlme_mgmt() instead.
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_auth_timeout - notification of timed out authentication
+ * cfg80211_auth_timeout - notification of timed out authentication
  * @dev: network device
  * @addr: The MAC address of the device with which the authentication timed out
  *
  * This function may sleep. The caller must hold the corresponding wdev's
  * mutex.
  */
-void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr);
 
 /**
- * cfg80211_send_rx_assoc - notification of processed association
+ * cfg80211_rx_assoc_resp - notification of processed association response
  * @dev: network device
- * @bss: the BSS struct association was requested for, the struct reference
- *     is owned by cfg80211 after this call
- * @buf: (re)association response frame (header + body)
+ * @bss: the BSS that association was requested with, ownership of the pointer
+ *     moves to cfg80211 in this call
+ * @buf: authentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever a (re)association response has been
- * processed in station mode. The driver is required to call either this
- * function or cfg80211_send_assoc_timeout() to indicate the result of
- * cfg80211_ops::assoc() call. This function may sleep. The caller must hold
- * the corresponding wdev's mutex.
+ * After being asked to associate via cfg80211_ops::assoc() the driver must
+ * call either this function or cfg80211_auth_timeout().
+ *
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
+void cfg80211_rx_assoc_resp(struct net_device *dev,
+                           struct cfg80211_bss *bss,
                            const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_assoc_timeout - notification of timed out association
+ * cfg80211_assoc_timeout - notification of timed out association
  * @dev: network device
  * @addr: The MAC address of the device with which the association timed out
  *
  * This function may sleep. The caller must hold the corresponding wdev's mutex.
  */
-void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
+void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr);
 
 /**
- * cfg80211_send_deauth - notification of processed deauthentication
+ * cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
  * @dev: network device
- * @buf: deauthentication frame (header + body)
+ * @buf: 802.11 frame (header + body)
  * @len: length of the frame data
  *
  * This function is called whenever deauthentication has been processed in
@@ -3492,46 +3509,20 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
  * locally generated ones. This function may sleep. The caller must hold the
  * corresponding wdev's mutex.
  */
-void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
-
-/**
- * cfg80211_send_disassoc - notification of processed disassociation
- * @dev: network device
- * @buf: disassociation response frame (header + body)
- * @len: length of the frame data
- *
- * This function is called whenever disassociation has been processed in
- * station mode. This includes both received disassociation frames and locally
- * generated ones. This function may sleep. The caller must hold the
- * corresponding wdev's mutex.
- */
-void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len);
 
 /**
- * cfg80211_send_unprot_deauth - notification of unprotected deauthentication
+ * cfg80211_rx_unprot_mlme_mgmt - notification of unprotected mlme mgmt frame
  * @dev: network device
  * @buf: deauthentication frame (header + body)
  * @len: length of the frame data
  *
- * This function is called whenever a received Deauthentication frame has been
- * dropped in station mode because of MFP being used but the Deauthentication
- * frame was not protected. This function may sleep.
- */
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len);
-
-/**
- * cfg80211_send_unprot_disassoc - notification of unprotected disassociation
- * @dev: network device
- * @buf: disassociation frame (header + body)
- * @len: length of the frame data
- *
- * This function is called whenever a received Disassociation frame has been
- * dropped in station mode because of MFP being used but the Disassociation
+ * This function is called whenever a received deauthentication or dissassoc
+ * frame has been dropped in station mode because of MFP being used but the
  * frame was not protected. This function may sleep.
  */
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len);
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev,
+                                 const u8 *buf, size_t len);
 
 /**
  * cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP)
index e07feb4..736b5fb 100644 (file)
@@ -50,7 +50,7 @@ struct inet6_ifaddr {
 
        int                     state;
 
-       __u8                    probes;
+       __u8                    dad_probes;
        __u8                    flags;
 
        __u16                   scope;
@@ -58,7 +58,7 @@ struct inet6_ifaddr {
        unsigned long           cstamp; /* created timestamp */
        unsigned long           tstamp; /* updated timestamp */
 
-       struct timer_list       timer;
+       struct timer_list       dad_timer;
 
        struct inet6_dev        *idev;
        struct rt6_info         *rt;
@@ -166,6 +166,7 @@ struct inet6_dev {
        struct net_device       *dev;
 
        struct list_head        addr_list;
+       int                     valid_ll_addr_cnt;
 
        struct ifmcaddr6        *mc_list;
        struct ifmcaddr6        *mc_tomb;
@@ -173,10 +174,12 @@ struct inet6_dev {
        unsigned char           mc_qrv;
        unsigned char           mc_gq_running;
        unsigned char           mc_ifc_count;
+       unsigned char           mc_dad_count;
        unsigned long           mc_v1_seen;
        unsigned long           mc_maxdelay;
        struct timer_list       mc_gq_timer;    /* general query timer */
        struct timer_list       mc_ifc_timer;   /* interface change timer */
+       struct timer_list       mc_dad_timer;   /* dad complete mc timer */
 
        struct ifacaddr6        *ac_list;
        rwlock_t                lock;
@@ -195,6 +198,10 @@ struct inet6_dev {
        struct neigh_parms      *nd_parms;
        struct ipv6_devconf     cnf;
        struct ipv6_devstat     stats;
+
+       struct timer_list       rs_timer;
+       __u8                    rs_probes;
+
        unsigned long           tstamp; /* ipv6InterfaceTable update timestamp */
        struct rcu_head         rcu;
 };
index 44424e9..aac8553 100644 (file)
@@ -56,7 +56,8 @@ struct fib_nh_exception {
        u32                             fnhe_pmtu;
        __be32                          fnhe_gw;
        unsigned long                   fnhe_expires;
-       struct rtable __rcu             *fnhe_rth;
+       struct rtable __rcu             *fnhe_rth_input;
+       struct rtable __rcu             *fnhe_rth_output;
        unsigned long                   fnhe_stamp;
 };
 
index 10bbb42..781b3cf 100644 (file)
@@ -42,6 +42,7 @@ struct ip_tunnel {
        struct ip_tunnel __rcu  *next;
        struct hlist_node hash_node;
        struct net_device       *dev;
+       struct net              *net;   /* netns for packet i/o */
 
        int             err_count;      /* Number of arrived ICMP errors */
        unsigned long   err_time;       /* Time when the last ICMP error
@@ -93,6 +94,8 @@ struct ip_tunnel_net {
        struct net_device *fb_tunnel_dev;
 };
 
+#ifdef CONFIG_INET
+
 int ip_tunnel_init(struct net_device *dev);
 void ip_tunnel_uninit(struct net_device *dev);
 void  ip_tunnel_dellink(struct net_device *dev, struct list_head *head);
@@ -180,4 +183,7 @@ static inline void iptunnel_xmit_stats(int err,
                err_stats->tx_dropped++;
        }
 }
+
+#endif /* CONFIG_INET */
+
 #endif /* __NET_IP_TUNNELS_H */
index fcc7c36..5bf2b3a 100644 (file)
@@ -30,6 +30,7 @@
 #ifdef CONFIG_NET_LL_RX_POLL
 
 struct napi_struct;
+extern unsigned int sysctl_net_ll_read __read_mostly;
 extern unsigned int sysctl_net_ll_poll __read_mostly;
 
 /* return values from ndo_ll_poll */
@@ -38,17 +39,18 @@ extern unsigned int sysctl_net_ll_poll __read_mostly;
 
 /* we can use sched_clock() because we don't care much about precision
  * we only care that the average is bounded
+ * we don't mind a ~2.5% imprecision so <<10 instead of *1000
+ * sk->sk_ll_usec is a u_int so this can't overflow
  */
-static inline u64 ll_end_time(struct sock *sk)
+static inline u64 ll_sk_end_time(struct sock *sk)
 {
-       u64 end_time = ACCESS_ONCE(sk->sk_ll_usec);
-
-       /* we don't mind a ~2.5% imprecision
-        * sk->sk_ll_usec is a u_int so this can't overflow
-        */
-       end_time = (end_time << 10) + sched_clock();
+       return ((u64)ACCESS_ONCE(sk->sk_ll_usec) << 10) + sched_clock();
+}
 
-       return end_time;
+/* in poll/select we use the global sysctl_net_ll_poll value */
+static inline u64 ll_end_time(void)
+{
+       return ((u64)ACCESS_ONCE(sysctl_net_ll_poll) << 10) + sched_clock();
 }
 
 static inline bool sk_valid_ll(struct sock *sk)
@@ -62,10 +64,13 @@ static inline bool can_poll_ll(u64 end_time)
        return !time_after64(sched_clock(), end_time);
 }
 
+/* when used in sock_poll() nonblock is known at compile time to be true
+ * so the loop and end_time will be optimized out
+ */
 static inline bool sk_poll_ll(struct sock *sk, int nonblock)
 {
+       u64 end_time = nonblock ? 0 : ll_sk_end_time(sk);
        const struct net_device_ops *ops;
-       u64 end_time = ll_end_time(sk);
        struct napi_struct *napi;
        int rc = false;
 
@@ -84,7 +89,6 @@ static inline bool sk_poll_ll(struct sock *sk, int nonblock)
                goto out;
 
        do {
-
                rc = ops->ndo_ll_poll(napi);
 
                if (rc == LL_FLUSH_FAILED)
@@ -95,8 +99,8 @@ static inline bool sk_poll_ll(struct sock *sk, int nonblock)
                        NET_ADD_STATS_BH(sock_net(sk),
                                         LINUX_MIB_LOWLATENCYRXPACKETS, rc);
 
-       } while (skb_queue_empty(&sk->sk_receive_queue)
-                       && can_poll_ll(end_time) && !nonblock);
+       } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
+                can_poll_ll(end_time));
 
        rc = !skb_queue_empty(&sk->sk_receive_queue);
 out:
@@ -118,7 +122,12 @@ static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb)
 
 #else /* CONFIG_NET_LL_RX_POLL */
 
-static inline u64 ll_end_time(struct sock *sk)
+static inline u64 sk_ll_end_time(struct sock *sk)
+{
+       return 0;
+}
+
+static inline u64 ll_end_time(void)
 {
        return 0;
 }
index 1f0014b..a405a7a 100644 (file)
@@ -217,8 +217,8 @@ struct ieee80211_chanctx_conf {
  * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
  * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
  *     changed (currently only in P2P client mode, GO mode will be later)
- * @BSS_CHANGED_DTIM_PERIOD: the DTIM period value was changed (set when
- *     it becomes valid, managed mode only)
+ * @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available:
+ *     currently dtim_period only is under consideration.
  * @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
  *     note that this is only called when it changes after the channel
  *     context had been assigned.
@@ -244,7 +244,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_PS                  = 1<<17,
        BSS_CHANGED_TXPOWER             = 1<<18,
        BSS_CHANGED_P2P_PS              = 1<<19,
-       BSS_CHANGED_DTIM_PERIOD         = 1<<20,
+       BSS_CHANGED_BEACON_INFO         = 1<<20,
        BSS_CHANGED_BANDWIDTH           = 1<<21,
 
        /* when adding here, make sure to change ieee80211_reconfig */
@@ -288,7 +288,7 @@ enum ieee80211_rssi_event {
  *     IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
  * @dtim_period: num of beacons before the next DTIM, for beaconing,
  *     valid in station mode only if after the driver was notified
- *     with the %BSS_CHANGED_DTIM_PERIOD flag, will be non-zero then.
+ *     with the %BSS_CHANGED_BEACON_INFO flag, will be non-zero then.
  * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old
  *     as it may have been received during scanning long ago). If the
  *     HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can
@@ -460,6 +460,8 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_CTL_DONTFRAG: Don't fragment this packet even if it
  *     would be fragmented by size (this is optional, only used for
  *     monitor injection).
+ * @IEEE80211_TX_CTL_PS_RESPONSE: This frame is a response to a poll
+ *     frame (PS-Poll or uAPSD).
  *
  * Note: If you have to add new flags to the enumeration, then don't
  *      forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -495,6 +497,7 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_STATUS_EOSP                = BIT(28),
        IEEE80211_TX_CTL_USE_MINRATE            = BIT(29),
        IEEE80211_TX_CTL_DONTFRAG               = BIT(30),
+       IEEE80211_TX_CTL_PS_RESPONSE            = BIT(31),
 };
 
 #define IEEE80211_TX_CTL_STBC_SHIFT            23
index b87a169..0af851c 100644 (file)
@@ -59,8 +59,10 @@ struct nfc_hci_ops {
                              struct nfc_target *target);
        int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
                              struct sk_buff *skb);
-       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
-       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*fw_upload)(struct nfc_hci_dev *hdev, const char *firmware_name);
+       int (*discover_se)(struct nfc_hci_dev *dev);
+       int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
+       int (*disable_se)(struct nfc_hci_dev *dev, u32 se_idx);
 };
 
 /* Pipes */
@@ -152,7 +154,6 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            unsigned long quirks,
                                            u32 protocols,
-                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
index 5bc0c46..99fc1f3 100644 (file)
@@ -3,6 +3,7 @@
  *  NFC Controller (NFCC) and a Device Host (DH).
  *
  *  Copyright (C) 2011 Texas Instruments, Inc.
+ *  Copyright (C) 2013 Intel Corporation. All rights reserved.
  *
  *  Written by Ilan Elias <ilane@ti.com>
  *
@@ -66,7 +67,7 @@ struct nci_dev;
 struct nci_ops {
        int (*open)(struct nci_dev *ndev);
        int (*close)(struct nci_dev *ndev);
-       int (*send)(struct sk_buff *skb);
+       int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES                4
@@ -147,13 +148,12 @@ struct nci_dev {
 /* ----- NCI Devices ----- */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
-                                   __u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 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 sk_buff *skb);
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
                                            unsigned int len,
@@ -202,4 +202,56 @@ void nci_req_complete(struct nci_dev *ndev, int result);
 /* ----- NCI status code ----- */
 int nci_to_errno(__u8 code);
 
+/* ----- NCI over SPI acknowledge modes ----- */
+#define NCI_SPI_CRC_DISABLED   0x00
+#define NCI_SPI_CRC_ENABLED    0x01
+
+/* ----- NCI SPI structures ----- */
+struct nci_spi_dev;
+
+struct nci_spi_ops {
+       int (*open)(struct nci_spi_dev *ndev);
+       int (*close)(struct nci_spi_dev *ndev);
+       void (*assert_int)(struct nci_spi_dev *ndev);
+       void (*deassert_int)(struct nci_spi_dev *ndev);
+};
+
+struct nci_spi_dev {
+       struct nci_dev          *nci_dev;
+       struct spi_device       *spi;
+       struct nci_spi_ops      *ops;
+
+       unsigned int            xfer_udelay;    /* microseconds delay between
+                                                 transactions */
+       u8                      acknowledge_mode;
+
+       struct completion       req_completion;
+       u8                      req_result;
+
+       void                    *driver_data;
+};
+
+/* ----- NCI SPI Devices ----- */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+                                               struct nci_spi_ops *ops,
+                                               u32 supported_protocols,
+                                               u32 supported_se,
+                                               u8 acknowledge_mode,
+                                               unsigned int delay);
+void nci_spi_free_device(struct nci_spi_dev *ndev);
+int nci_spi_register_device(struct nci_spi_dev *ndev);
+void nci_spi_unregister_device(struct nci_spi_dev *ndev);
+int nci_spi_recv_frame(struct nci_spi_dev *ndev);
+
+static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev,
+                                           void *data)
+{
+       ndev->driver_data = data;
+}
+
+static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev)
+{
+       return ndev->driver_data;
+}
+
 #endif /* __NCI_CORE_H */
index 5eb80bb..0e353f1 100644 (file)
@@ -68,8 +68,12 @@ struct nfc_ops {
                             void *cb_context);
        int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
-       int (*enable_se)(struct nfc_dev *dev, u32 secure_element);
-       int (*disable_se)(struct nfc_dev *dev, u32 secure_element);
+       int (*fw_upload)(struct nfc_dev *dev, const char *firmware_name);
+
+       /* Secure Element API */
+       int (*discover_se)(struct nfc_dev *dev);
+       int (*enable_se)(struct nfc_dev *dev, u32 se_idx);
+       int (*disable_se)(struct nfc_dev *dev, u32 se_idx);
 };
 
 #define NFC_TARGET_IDX_ANY -1
@@ -83,6 +87,8 @@ struct nfc_target {
        u8 sel_res;
        u8 nfcid1_len;
        u8 nfcid1[NFC_NFCID1_MAXSIZE];
+       u8 nfcid2_len;
+       u8 nfcid2[NFC_NFCID2_MAXSIZE];
        u8 sensb_res_len;
        u8 sensb_res[NFC_SENSB_RES_MAXSIZE];
        u8 sensf_res_len;
@@ -91,6 +97,23 @@ struct nfc_target {
        u8 logical_idx;
 };
 
+/**
+ * nfc_se - A structure for NFC accessible secure elements.
+ *
+ * @idx: The secure element index. User space will enable or
+ *       disable a secure element by its index.
+ * @type: The secure element type. It can be SE_UICC or
+ *        SE_EMBEDDED.
+ * @state: The secure element state, either enabled or disabled.
+ *
+ */
+struct nfc_se {
+       struct list_head list;
+       u32 idx;
+       u16 type;
+       u16 state;
+};
+
 struct nfc_genl_data {
        u32 poll_req_portid;
        struct mutex genl_data_mutex;
@@ -104,6 +127,7 @@ struct nfc_dev {
        int targets_generation;
        struct device dev;
        bool dev_up;
+       bool fw_upload_in_progress;
        u8 rf_mode;
        bool polling;
        struct nfc_target *active_target;
@@ -111,8 +135,7 @@ struct nfc_dev {
        struct nfc_genl_data genl_data;
        u32 supported_protocols;
 
-       u32 supported_se;
-       u32 active_se;
+       struct list_head secure_elements;
 
        int tx_headroom;
        int tx_tailroom;
@@ -132,7 +155,6 @@ extern struct class nfc_class;
 
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom,
                                    int tx_tailroom);
 
@@ -216,4 +238,7 @@ int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 
 void nfc_driver_failure(struct nfc_dev *dev, int err);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
+
 #endif /* __NET_NFC_H */
index 6321c08..e6b95bc 100644 (file)
@@ -188,11 +188,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly;
  *  Section:  Macros, externs, and inlines
  */
 
-
-#ifdef TEST_FRAME
-#include <test_frame.h>
-#else
-
 /* spin lock wrappers. */
 #define sctp_spin_lock_irqsave(lock, flags) spin_lock_irqsave(lock, flags)
 #define sctp_spin_unlock_irqrestore(lock, flags)  \
@@ -218,8 +213,6 @@ extern struct kmem_cache *sctp_bucket_cachep __read_mostly;
 #define SCTP_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)->sctp.sctp_statistics, field)
 #define SCTP_DEC_STATS(net, field)      SNMP_DEC_STATS((net)->sctp.sctp_statistics, field)
 
-#endif /* !TEST_FRAME */
-
 /* sctp mib definitions */
 enum {
        SCTP_MIB_NUM = 0,
@@ -567,24 +560,6 @@ for (pos = chunk->subh.fwdtsn_hdr->skip;\
 /* Round an int up to the next multiple of 4.  */
 #define WORD_ROUND(s) (((s)+3)&~3)
 
-/* Compare two timevals.  */
-#define tv_lt(s, t) \
-   (s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec))
-
-/* Add tv1 to tv2. */
-#define TIMEVAL_ADD(tv1, tv2) \
-({ \
-        suseconds_t usecs = (tv2).tv_usec + (tv1).tv_usec; \
-        time_t secs = (tv2).tv_sec + (tv1).tv_sec; \
-\
-        if (usecs >= 1000000) { \
-                usecs -= 1000000; \
-                secs++; \
-        } \
-        (tv2).tv_sec = secs; \
-        (tv2).tv_usec = usecs; \
-})
-
 /* External references. */
 
 extern struct proto sctp_prot;
index 1bd4c41..e745c92 100644 (file)
@@ -54,7 +54,7 @@
 #ifndef __sctp_structs_h__
 #define __sctp_structs_h__
 
-#include <linux/time.h>                /* We get struct timespec.    */
+#include <linux/ktime.h>
 #include <linux/socket.h>      /* linux/in.h needs this!!    */
 #include <linux/in.h>          /* We get struct sockaddr_in. */
 #include <linux/in6.h>         /* We get struct in6_addr     */
@@ -284,7 +284,7 @@ struct sctp_cookie {
        __u32 peer_ttag;
 
        /* When does this cookie expire? */
-       struct timeval expiration;
+       ktime_t expiration;
 
        /* Number of inbound/outbound streams which are set
         * and negotiated during the INIT process.
@@ -1537,7 +1537,7 @@ struct sctp_association {
        sctp_state_t state;
 
        /* The cookie life I award for any cookie.  */
-       struct timeval cookie_life;
+       ktime_t cookie_life;
 
        /* Overall     : The overall association error count.
         * Error Count : [Clear this any time I get something.]
index 6fa8083..d198005 100644 (file)
@@ -1319,9 +1319,9 @@ void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc);
 
 /* Fastopen key context */
 struct tcp_fastopen_context {
-       struct crypto_cipher __rcu      *tfm;
-       __u8                            key[TCP_FASTOPEN_KEY_LENGTH];
-       struct rcu_head                 rcu;
+       struct crypto_cipher    *tfm;
+       __u8                    key[TCP_FASTOPEN_KEY_LENGTH];
+       struct rcu_head         rcu;
 };
 
 /* write queue abstraction */
index 9ce7f44..4aee586 100644 (file)
@@ -30,6 +30,8 @@
 
 #define POLLFREE       0x4000  /* currently only for epoll */
 
+#define POLL_LL                0x8000
+
 struct pollfd {
        int fd;
        short events;
index 82c7d1b..d7fea34 100644 (file)
@@ -93,6 +93,7 @@
 #define ARPHRD_PHONET_PIPE 821         /* PhoNet pipe header           */
 #define ARPHRD_CAIF    822             /* CAIF media type              */
 #define ARPHRD_IP6GRE  823             /* GRE over IPv6                */
+#define ARPHRD_NETLINK 824             /* Netlink header               */
 
 #define ARPHRD_VOID      0xFFFF        /* Void type, nothing is known */
 #define ARPHRD_NONE      0xFFFE        /* zero header length */
index 7c6f627..caed0f3 100644 (file)
@@ -69,6 +69,8 @@
  *     starting a poll from a device which has a secure element enabled means
  *     we want to do SE based card emulation.
  * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
+ * @NFC_CMD_FW_UPLOAD: Request to Load/flash firmware, or event to inform that
+ *     some firmware was loaded
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -92,6 +94,9 @@ enum nfc_commands {
        NFC_CMD_DISABLE_SE,
        NFC_CMD_LLC_SDREQ,
        NFC_EVENT_LLC_SDRES,
+       NFC_CMD_FW_UPLOAD,
+       NFC_EVENT_SE_ADDED,
+       NFC_EVENT_SE_REMOVED,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -121,6 +126,9 @@ enum nfc_commands {
  * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter
  * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter
  * @NFC_ATTR_SE: Available Secure Elements
+ * @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
+ * @NFC_ATTR_SE_INDEX: Secure element index
+ * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -143,6 +151,9 @@ enum nfc_attrs {
        NFC_ATTR_LLC_PARAM_MIUX,
        NFC_ATTR_SE,
        NFC_ATTR_LLC_SDP,
+       NFC_ATTR_FIRMWARE_NAME,
+       NFC_ATTR_SE_INDEX,
+       NFC_ATTR_SE_TYPE,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
@@ -159,9 +170,12 @@ enum nfc_sdp_attr {
 
 #define NFC_DEVICE_NAME_MAXSIZE 8
 #define NFC_NFCID1_MAXSIZE 10
+#define NFC_NFCID2_MAXSIZE 8
+#define NFC_NFCID3_MAXSIZE 10
 #define NFC_SENSB_RES_MAXSIZE 12
 #define NFC_SENSF_RES_MAXSIZE 18
 #define NFC_GB_MAXSIZE        48
+#define NFC_FIRMWARE_NAME_MAXSIZE 32
 
 /* NFC protocols */
 #define NFC_PROTO_JEWEL                1
@@ -191,10 +205,12 @@ enum nfc_sdp_attr {
 #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B)
 
 /* NFC Secure Elements */
-#define NFC_SE_NONE     0x0
 #define NFC_SE_UICC     0x1
 #define NFC_SE_EMBEDDED 0x2
 
+#define NFC_SE_DISABLED 0x0
+#define NFC_SE_ENABLED  0x1
+
 struct sockaddr_nfc {
        sa_family_t sa_family;
        __u32 dev_idx;
index 5920715..ca6facf 100644 (file)
@@ -2577,6 +2577,10 @@ enum nl80211_mesh_power_mode {
  *
  * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
  *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ *     established peering with for longer than this time (in seconds), then
+ *     remove it from the STA's list of peers.  Default is 30 minutes.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2608,6 +2612,7 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
        NL80211_MESHCONF_POWER_MODE,
        NL80211_MESHCONF_AWAKE_WINDOW,
+       NL80211_MESHCONF_PLINK_TIMEOUT,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -3579,6 +3584,10 @@ enum nl80211_ap_sme_features {
  *     Peering Management entity which may be implemented by registering for
  *     beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
  *     still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ *     interface. An active monitor interface behaves like a normal monitor
+ *     interface, but gets added to the driver. It ensures that incoming
+ *     unicast packets directed at the configured interface address get ACKed.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
index 26cbf76..af0a674 100644 (file)
@@ -288,6 +288,7 @@ enum
        LINUX_MIB_XFRMOUTPOLERROR,              /* XfrmOutPolError */
        LINUX_MIB_XFRMFWDHDRERROR,              /* XfrmFwdHdrError*/
        LINUX_MIB_XFRMOUTSTATEINVALID,          /* XfrmOutStateInvalid */
+       LINUX_MIB_XFRMACQUIREERROR,             /* XfrmAcquireError */
        __LINUX_MIB_XFRMMAX
 };
 
index 51aafd6..08125f3 100644 (file)
@@ -473,7 +473,6 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
                crc = crc32c(crc, data, len);
                consumed += len;
        }
-       skb_abort_seq_read(&st);
 
        return htonl(crc);
 }
index 31952a1..81befac 100644 (file)
@@ -1012,7 +1012,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 #endif
 
 /*
- * Add port to rotuer_list
+ * Add port to router_list
  *  list is maintained ordered by pointer value
  *  and locked by br->multicast_lock and RCU
  */
index fa007db..370354a 100644 (file)
 #include <linux/cpu_rmap.h>
 #include <linux/static_key.h>
 #include <linux/hashtable.h>
+#include <linux/vmalloc.h>
 
 #include "net-sysfs.h"
 
@@ -1651,22 +1652,13 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
                }
        }
 
-       skb_orphan(skb);
-
        if (unlikely(!is_skb_forwardable(dev, skb))) {
                atomic_long_inc(&dev->rx_dropped);
                kfree_skb(skb);
                return NET_RX_DROP;
        }
-       skb->skb_iif = 0;
-       skb_dst_drop(skb);
-       skb->tstamp.tv64 = 0;
-       skb->pkt_type = PACKET_HOST;
+       skb_scrub_packet(skb);
        skb->protocol = eth_type_trans(skb, dev);
-       skb->mark = 0;
-       secpath_reset(skb);
-       nf_reset(skb);
-       nf_reset_trace(skb);
        return netif_rx(skb);
 }
 EXPORT_SYMBOL_GPL(dev_forward_skb);
@@ -5253,17 +5245,28 @@ static void netdev_init_one_queue(struct net_device *dev,
 #endif
 }
 
+static void netif_free_tx_queues(struct net_device *dev)
+{
+       if (is_vmalloc_addr(dev->_tx))
+               vfree(dev->_tx);
+       else
+               kfree(dev->_tx);
+}
+
 static int netif_alloc_netdev_queues(struct net_device *dev)
 {
        unsigned int count = dev->num_tx_queues;
        struct netdev_queue *tx;
+       size_t sz = count * sizeof(*tx);
 
-       BUG_ON(count < 1);
-
-       tx = kcalloc(count, sizeof(struct netdev_queue), GFP_KERNEL);
-       if (!tx)
-               return -ENOMEM;
+       BUG_ON(count < 1 || count > 0xffff);
 
+       tx = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+       if (!tx) {
+               tx = vzalloc(sz);
+               if (!tx)
+                       return -ENOMEM;
+       }
        dev->_tx = tx;
 
        netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL);
@@ -5811,7 +5814,7 @@ free_all:
 
 free_pcpu:
        free_percpu(dev->pcpu_refcnt);
-       kfree(dev->_tx);
+       netif_free_tx_queues(dev);
 #ifdef CONFIG_RPS
        kfree(dev->_rx);
 #endif
@@ -5836,7 +5839,7 @@ void free_netdev(struct net_device *dev)
 
        release_net(dev_net(dev));
 
-       kfree(dev->_tx);
+       netif_free_tx_queues(dev);
 #ifdef CONFIG_RPS
        kfree(dev->_rx);
 #endif
index edf3757..b1fcb87 100644 (file)
@@ -2541,8 +2541,13 @@ unsigned int skb_seq_read(unsigned int consumed, const u8 **data,
        unsigned int block_limit, abs_offset = consumed + st->lower_offset;
        skb_frag_t *frag;
 
-       if (unlikely(abs_offset >= st->upper_offset))
+       if (unlikely(abs_offset >= st->upper_offset)) {
+               if (st->frag_data) {
+                       kunmap_atomic(st->frag_data);
+                       st->frag_data = NULL;
+               }
                return 0;
+       }
 
 next_skb:
        block_limit = skb_headlen(st->cur_skb) + st->stepped_offset;
@@ -3487,3 +3492,26 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
        return true;
 }
 EXPORT_SYMBOL(skb_try_coalesce);
+
+/**
+ * skb_scrub_packet - scrub an skb before sending it to another netns
+ *
+ * @skb: buffer to clean
+ *
+ * skb_scrub_packet can be used to clean an skb before injecting it in
+ * another namespace. We have to clear all information in the skb that
+ * could impact namespace isolation.
+ */
+void skb_scrub_packet(struct sk_buff *skb)
+{
+       skb_orphan(skb);
+       skb->tstamp.tv64 = 0;
+       skb->pkt_type = PACKET_HOST;
+       skb->skb_iif = 0;
+       skb_dst_drop(skb);
+       skb->mark = 0;
+       secpath_reset(skb);
+       nf_reset(skb);
+       nf_reset_trace(skb);
+}
+EXPORT_SYMBOL_GPL(skb_scrub_packet);
index 1e744b1..b6c619f 100644 (file)
@@ -2307,7 +2307,7 @@ void sock_init_data(struct socket *sock, struct sock *sk)
 
 #ifdef CONFIG_NET_LL_RX_POLL
        sk->sk_napi_id          =       0;
-       sk->sk_ll_usec          =       sysctl_net_ll_poll;
+       sk->sk_ll_usec          =       sysctl_net_ll_read;
 #endif
 
        /*
index 62702c2..afc677e 100644 (file)
@@ -306,6 +306,14 @@ static struct ctl_table net_core_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "low_latency_read",
+               .data           = &sysctl_net_ll_read,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
+#
 #endif
 #endif /* CONFIG_NET */
        {
index 05a4888..b3f627a 100644 (file)
@@ -961,7 +961,7 @@ static void nl_fib_input(struct sk_buff *skb)
            nlmsg_len(nlh) < sizeof(*frn))
                return;
 
-       skb = skb_clone(skb, GFP_KERNEL);
+       skb = netlink_skb_clone(skb, GFP_KERNEL);
        if (skb == NULL)
                return;
        nlh = nlmsg_hdr(skb);
index 8f6cb7a..d5dbca5 100644 (file)
@@ -169,7 +169,8 @@ static void free_nh_exceptions(struct fib_nh *nh)
                        
                        next = rcu_dereference_protected(fnhe->fnhe_next, 1);
 
-                       rt_fibinfo_free(&fnhe->fnhe_rth);
+                       rt_fibinfo_free(&fnhe->fnhe_rth_input);
+                       rt_fibinfo_free(&fnhe->fnhe_rth_output);
 
                        kfree(fnhe);
 
index bd227e5..394cebc 100644 (file)
@@ -304,6 +304,7 @@ static struct net_device *__ip_tunnel_create(struct net *net,
 
        tunnel = netdev_priv(dev);
        tunnel->parms = *parms;
+       tunnel->net = net;
 
        err = register_netdevice(dev);
        if (err)
@@ -453,6 +454,9 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
        tstats->rx_bytes += skb->len;
        u64_stats_update_end(&tstats->syncp);
 
+       if (tunnel->net != dev_net(tunnel->dev))
+               skb_scrub_packet(skb);
+
        if (tunnel->dev->type == ARPHRD_ETHER) {
                skb->protocol = eth_type_trans(skb, tunnel->dev);
                skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
@@ -541,8 +545,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                        tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph);
        }
 
-       rt = ip_route_output_tunnel(dev_net(dev), &fl4,
-                                   tunnel->parms.iph.protocol,
+       rt = ip_route_output_tunnel(tunnel->net, &fl4,
+                                   protocol,
                                    dst, tnl_params->saddr,
                                    tunnel->parms.o_key,
                                    RT_TOS(tos),
@@ -602,6 +606,9 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
        }
 #endif
 
+       if (tunnel->net != dev_net(dev))
+               skb_scrub_packet(skb);
+
        if (tunnel->err_count > 0) {
                if (time_before(jiffies,
                                tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
@@ -888,6 +895,7 @@ int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[],
        if (ip_tunnel_find(itn, p, dev->type))
                return -EEXIST;
 
+       nt->net = net;
        nt->parms = *p;
        err = register_netdevice(dev);
        if (err)
index f3fa42e..a9a54a2 100644 (file)
@@ -565,10 +565,25 @@ static inline void rt_free(struct rtable *rt)
 
 static DEFINE_SPINLOCK(fnhe_lock);
 
+static void fnhe_flush_routes(struct fib_nh_exception *fnhe)
+{
+       struct rtable *rt;
+
+       rt = rcu_dereference(fnhe->fnhe_rth_input);
+       if (rt) {
+               RCU_INIT_POINTER(fnhe->fnhe_rth_input, NULL);
+               rt_free(rt);
+       }
+       rt = rcu_dereference(fnhe->fnhe_rth_output);
+       if (rt) {
+               RCU_INIT_POINTER(fnhe->fnhe_rth_output, NULL);
+               rt_free(rt);
+       }
+}
+
 static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash)
 {
        struct fib_nh_exception *fnhe, *oldest;
-       struct rtable *orig;
 
        oldest = rcu_dereference(hash->chain);
        for (fnhe = rcu_dereference(oldest->fnhe_next); fnhe;
@@ -576,11 +591,7 @@ static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash)
                if (time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp))
                        oldest = fnhe;
        }
-       orig = rcu_dereference(oldest->fnhe_rth);
-       if (orig) {
-               RCU_INIT_POINTER(oldest->fnhe_rth, NULL);
-               rt_free(orig);
-       }
+       fnhe_flush_routes(oldest);
        return oldest;
 }
 
@@ -644,7 +655,10 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
                        fnhe->fnhe_expires = max(1UL, expires);
                }
                /* Update all cached dsts too */
-               rt = rcu_dereference(fnhe->fnhe_rth);
+               rt = rcu_dereference(fnhe->fnhe_rth_input);
+               if (rt)
+                       fill_route_from_fnhe(rt, fnhe);
+               rt = rcu_dereference(fnhe->fnhe_rth_output);
                if (rt)
                        fill_route_from_fnhe(rt, fnhe);
        } else {
@@ -668,6 +682,10 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw,
                 * stale, so anyone caching it rechecks if this exception
                 * applies to them.
                 */
+               rt = rcu_dereference(nh->nh_rth_input);
+               if (rt)
+                       rt->dst.obsolete = DST_OBSOLETE_KILL;
+
                for_each_possible_cpu(i) {
                        struct rtable __rcu **prt;
                        prt = per_cpu_ptr(nh->nh_pcpu_rth_output, i);
@@ -1242,25 +1260,36 @@ static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe,
        spin_lock_bh(&fnhe_lock);
 
        if (daddr == fnhe->fnhe_daddr) {
+               struct rtable __rcu **porig;
+               struct rtable *orig;
                int genid = fnhe_genid(dev_net(rt->dst.dev));
-               struct rtable *orig = rcu_dereference(fnhe->fnhe_rth);
+
+               if (rt_is_input_route(rt))
+                       porig = &fnhe->fnhe_rth_input;
+               else
+                       porig = &fnhe->fnhe_rth_output;
+               orig = rcu_dereference(*porig);
 
                if (fnhe->fnhe_genid != genid) {
                        fnhe->fnhe_genid = genid;
                        fnhe->fnhe_gw = 0;
                        fnhe->fnhe_pmtu = 0;
                        fnhe->fnhe_expires = 0;
+                       fnhe_flush_routes(fnhe);
+                       orig = NULL;
                }
                fill_route_from_fnhe(rt, fnhe);
                if (!rt->rt_gateway)
                        rt->rt_gateway = daddr;
 
-               rcu_assign_pointer(fnhe->fnhe_rth, rt);
-               if (orig)
-                       rt_free(orig);
+               if (!(rt->dst.flags & DST_NOCACHE)) {
+                       rcu_assign_pointer(*porig, rt);
+                       if (orig)
+                               rt_free(orig);
+                       ret = true;
+               }
 
                fnhe->fnhe_stamp = jiffies;
-               ret = true;
        }
        spin_unlock_bh(&fnhe_lock);
 
@@ -1492,6 +1521,7 @@ static int __mkroute_input(struct sk_buff *skb,
                           struct in_device *in_dev,
                           __be32 daddr, __be32 saddr, u32 tos)
 {
+       struct fib_nh_exception *fnhe;
        struct rtable *rth;
        int err;
        struct in_device *out_dev;
@@ -1538,8 +1568,13 @@ static int __mkroute_input(struct sk_buff *skb,
                }
        }
 
+       fnhe = find_exception(&FIB_RES_NH(*res), daddr);
        if (do_cache) {
-               rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
+               if (fnhe != NULL)
+                       rth = rcu_dereference(fnhe->fnhe_rth_input);
+               else
+                       rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
+
                if (rt_cache_valid(rth)) {
                        skb_dst_set_noref(skb, &rth->dst);
                        goto out;
@@ -1567,7 +1602,7 @@ static int __mkroute_input(struct sk_buff *skb,
        rth->dst.input = ip_forward;
        rth->dst.output = ip_output;
 
-       rt_set_nexthop(rth, daddr, res, NULL, res->fi, res->type, itag);
+       rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag);
        skb_dst_set(skb, &rth->dst);
 out:
        err = 0;
@@ -1882,7 +1917,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
 
                fnhe = find_exception(nh, fl4->daddr);
                if (fnhe)
-                       prth = &fnhe->fnhe_rth;
+                       prth = &fnhe->fnhe_rth_output;
                else {
                        if (unlikely(fl4->flowi4_flags &
                                     FLOWI_FLAG_KNOWN_NH &&
index 8044912..12dd2fe 100644 (file)
@@ -253,37 +253,32 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev)
        return !qdisc_tx_is_noop(dev);
 }
 
-static void addrconf_del_timer(struct inet6_ifaddr *ifp)
+static void addrconf_del_rs_timer(struct inet6_dev *idev)
 {
-       if (del_timer(&ifp->timer))
+       if (del_timer(&idev->rs_timer))
+               __in6_dev_put(idev);
+}
+
+static void addrconf_del_dad_timer(struct inet6_ifaddr *ifp)
+{
+       if (del_timer(&ifp->dad_timer))
                __in6_ifa_put(ifp);
 }
 
-enum addrconf_timer_t {
-       AC_NONE,
-       AC_DAD,
-       AC_RS,
-};
+static void addrconf_mod_rs_timer(struct inet6_dev *idev,
+                                 unsigned long when)
+{
+       if (!timer_pending(&idev->rs_timer))
+               in6_dev_hold(idev);
+       mod_timer(&idev->rs_timer, jiffies + when);
+}
 
-static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
-                              enum addrconf_timer_t what,
-                              unsigned long when)
+static void addrconf_mod_dad_timer(struct inet6_ifaddr *ifp,
+                                  unsigned long when)
 {
-       if (!del_timer(&ifp->timer))
+       if (!timer_pending(&ifp->dad_timer))
                in6_ifa_hold(ifp);
-
-       switch (what) {
-       case AC_DAD:
-               ifp->timer.function = addrconf_dad_timer;
-               break;
-       case AC_RS:
-               ifp->timer.function = addrconf_rs_timer;
-               break;
-       default:
-               break;
-       }
-       ifp->timer.expires = jiffies + when;
-       add_timer(&ifp->timer);
+       mod_timer(&ifp->dad_timer, jiffies + when);
 }
 
 static int snmp6_alloc_dev(struct inet6_dev *idev)
@@ -326,6 +321,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
 
        WARN_ON(!list_empty(&idev->addr_list));
        WARN_ON(idev->mc_list != NULL);
+       WARN_ON(timer_pending(&idev->rs_timer));
 
 #ifdef NET_REFCNT_DEBUG
        pr_debug("%s: %s\n", __func__, dev ? dev->name : "NIL");
@@ -357,7 +353,8 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
        rwlock_init(&ndev->lock);
        ndev->dev = dev;
        INIT_LIST_HEAD(&ndev->addr_list);
-
+       setup_timer(&ndev->rs_timer, addrconf_rs_timer,
+                   (unsigned long)ndev);
        memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
        ndev->cnf.mtu6 = dev->mtu;
        ndev->cnf.sysctl = NULL;
@@ -776,7 +773,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 
        in6_dev_put(ifp->idev);
 
-       if (del_timer(&ifp->timer))
+       if (del_timer(&ifp->dad_timer))
                pr_notice("Timer is still running, when freeing ifa=%p\n", ifp);
 
        if (ifp->state != INET6_IFADDR_STATE_DEAD) {
@@ -869,9 +866,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
        spin_lock_init(&ifa->lock);
        spin_lock_init(&ifa->state_lock);
-       init_timer(&ifa->timer);
+       setup_timer(&ifa->dad_timer, addrconf_dad_timer,
+                   (unsigned long)ifa);
        INIT_HLIST_NODE(&ifa->addr_lst);
-       ifa->timer.data = (unsigned long) ifa;
        ifa->scope = scope;
        ifa->prefix_len = pfxlen;
        ifa->flags = flags | IFA_F_TENTATIVE;
@@ -994,7 +991,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        }
        write_unlock_bh(&idev->lock);
 
-       addrconf_del_timer(ifp);
+       addrconf_del_dad_timer(ifp);
 
        ipv6_ifa_notify(RTM_DELADDR, ifp);
 
@@ -1447,6 +1444,23 @@ try_nextdev:
 }
 EXPORT_SYMBOL(ipv6_dev_get_saddr);
 
+static int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
+                            unsigned char banned_flags)
+{
+       struct inet6_ifaddr *ifp;
+       int err = -EADDRNOTAVAIL;
+
+       list_for_each_entry(ifp, &idev->addr_list, if_list) {
+               if (ifp->scope == IFA_LINK &&
+                   !(ifp->flags & banned_flags)) {
+                       *addr = ifp->addr;
+                       err = 0;
+                       break;
+               }
+       }
+       return err;
+}
+
 int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
                    unsigned char banned_flags)
 {
@@ -1456,17 +1470,8 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
        rcu_read_lock();
        idev = __in6_dev_get(dev);
        if (idev) {
-               struct inet6_ifaddr *ifp;
-
                read_lock_bh(&idev->lock);
-               list_for_each_entry(ifp, &idev->addr_list, if_list) {
-                       if (ifp->scope == IFA_LINK &&
-                           !(ifp->flags & banned_flags)) {
-                               *addr = ifp->addr;
-                               err = 0;
-                               break;
-                       }
-               }
+               err = __ipv6_get_lladdr(idev, addr, banned_flags);
                read_unlock_bh(&idev->lock);
        }
        rcu_read_unlock();
@@ -1580,7 +1585,7 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
 {
        if (ifp->flags&IFA_F_PERMANENT) {
                spin_lock_bh(&ifp->lock);
-               addrconf_del_timer(ifp);
+               addrconf_del_dad_timer(ifp);
                ifp->flags |= IFA_F_TENTATIVE;
                if (dad_failed)
                        ifp->flags |= IFA_F_DADFAILED;
@@ -2502,12 +2507,6 @@ static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *p
                        read_unlock_bh(&idev->lock);
 
                        ipv6_del_addr(ifp);
-
-                       /* If the last address is deleted administratively,
-                          disable IPv6 on this interface.
-                        */
-                       if (list_empty(&idev->addr_list))
-                               addrconf_ifdown(idev->dev, 1);
                        return 0;
                }
        }
@@ -2760,8 +2759,6 @@ static void addrconf_gre_config(struct net_device *dev)
        struct inet6_dev *idev;
        struct in6_addr addr;
 
-       pr_info("%s(%s)\n", __func__, dev->name);
-
        ASSERT_RTNL();
 
        if ((idev = ipv6_find_idev(dev)) == NULL) {
@@ -3038,7 +3035,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                hlist_for_each_entry_rcu(ifa, h, addr_lst) {
                        if (ifa->idev == idev) {
                                hlist_del_init_rcu(&ifa->addr_lst);
-                               addrconf_del_timer(ifa);
+                               addrconf_del_dad_timer(ifa);
                                goto restart;
                        }
                }
@@ -3047,6 +3044,8 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        write_lock_bh(&idev->lock);
 
+       addrconf_del_rs_timer(idev);
+
        /* Step 2: clear flags for stateless addrconf */
        if (!how)
                idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
@@ -3076,7 +3075,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        while (!list_empty(&idev->addr_list)) {
                ifa = list_first_entry(&idev->addr_list,
                                       struct inet6_ifaddr, if_list);
-               addrconf_del_timer(ifa);
+               addrconf_del_dad_timer(ifa);
 
                list_del(&ifa->if_list);
 
@@ -3118,10 +3117,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
 static void addrconf_rs_timer(unsigned long data)
 {
-       struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
-       struct inet6_dev *idev = ifp->idev;
+       struct inet6_dev *idev = (struct inet6_dev *)data;
+       struct in6_addr lladdr;
 
-       read_lock(&idev->lock);
+       write_lock(&idev->lock);
        if (idev->dead || !(idev->if_flags & IF_READY))
                goto out;
 
@@ -3132,18 +3131,19 @@ static void addrconf_rs_timer(unsigned long data)
        if (idev->if_flags & IF_RA_RCVD)
                goto out;
 
-       spin_lock(&ifp->lock);
-       if (ifp->probes++ < idev->cnf.rtr_solicits) {
-               /* The wait after the last probe can be shorter */
-               addrconf_mod_timer(ifp, AC_RS,
-                                  (ifp->probes == idev->cnf.rtr_solicits) ?
-                                  idev->cnf.rtr_solicit_delay :
-                                  idev->cnf.rtr_solicit_interval);
-               spin_unlock(&ifp->lock);
+       if (idev->rs_probes++ < idev->cnf.rtr_solicits) {
+               if (!__ipv6_get_lladdr(idev, &lladdr, IFA_F_TENTATIVE))
+                       ndisc_send_rs(idev->dev, &lladdr,
+                                     &in6addr_linklocal_allrouters);
+               else
+                       goto out;
 
-               ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+               /* The wait after the last probe can be shorter */
+               addrconf_mod_rs_timer(idev, (idev->rs_probes ==
+                                            idev->cnf.rtr_solicits) ?
+                                     idev->cnf.rtr_solicit_delay :
+                                     idev->cnf.rtr_solicit_interval);
        } else {
-               spin_unlock(&ifp->lock);
                /*
                 * Note: we do not support deprecated "all on-link"
                 * assumption any longer.
@@ -3152,8 +3152,8 @@ static void addrconf_rs_timer(unsigned long data)
        }
 
 out:
-       read_unlock(&idev->lock);
-       in6_ifa_put(ifp);
+       write_unlock(&idev->lock);
+       in6_dev_put(idev);
 }
 
 /*
@@ -3169,8 +3169,8 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
        else
                rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
 
-       ifp->probes = idev->cnf.dad_transmits;
-       addrconf_mod_timer(ifp, AC_DAD, rand_num);
+       ifp->dad_probes = idev->cnf.dad_transmits;
+       addrconf_mod_dad_timer(ifp, rand_num);
 }
 
 static void addrconf_dad_start(struct inet6_ifaddr *ifp)
@@ -3231,40 +3231,40 @@ static void addrconf_dad_timer(unsigned long data)
        struct inet6_dev *idev = ifp->idev;
        struct in6_addr mcaddr;
 
-       if (!ifp->probes && addrconf_dad_end(ifp))
+       if (!ifp->dad_probes && addrconf_dad_end(ifp))
                goto out;
 
-       read_lock(&idev->lock);
+       write_lock(&idev->lock);
        if (idev->dead || !(idev->if_flags & IF_READY)) {
-               read_unlock(&idev->lock);
+               write_unlock(&idev->lock);
                goto out;
        }
 
        spin_lock(&ifp->lock);
        if (ifp->state == INET6_IFADDR_STATE_DEAD) {
                spin_unlock(&ifp->lock);
-               read_unlock(&idev->lock);
+               write_unlock(&idev->lock);
                goto out;
        }
 
-       if (ifp->probes == 0) {
+       if (ifp->dad_probes == 0) {
                /*
                 * DAD was successful
                 */
 
                ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
                spin_unlock(&ifp->lock);
-               read_unlock(&idev->lock);
+               write_unlock(&idev->lock);
 
                addrconf_dad_completed(ifp);
 
                goto out;
        }
 
-       ifp->probes--;
-       addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
+       ifp->dad_probes--;
+       addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time);
        spin_unlock(&ifp->lock);
-       read_unlock(&idev->lock);
+       write_unlock(&idev->lock);
 
        /* send a neighbour solicitation for our addr */
        addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
@@ -3276,6 +3276,10 @@ out:
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 {
        struct net_device *dev = ifp->idev->dev;
+       struct in6_addr lladdr;
+       bool send_rs, send_mld;
+
+       addrconf_del_dad_timer(ifp);
 
        /*
         *      Configure the address for reception. Now it is valid.
@@ -3287,22 +3291,41 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
           router advertisements, start sending router solicitations.
         */
 
-       if (ipv6_accept_ra(ifp->idev) &&
-           ifp->idev->cnf.rtr_solicits > 0 &&
-           (dev->flags&IFF_LOOPBACK) == 0 &&
-           (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+       read_lock_bh(&ifp->idev->lock);
+       spin_lock(&ifp->lock);
+       send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL &&
+                  ifp->idev->valid_ll_addr_cnt == 1;
+       send_rs = send_mld &&
+                 ipv6_accept_ra(ifp->idev) &&
+                 ifp->idev->cnf.rtr_solicits > 0 &&
+                 (dev->flags&IFF_LOOPBACK) == 0;
+       spin_unlock(&ifp->lock);
+       read_unlock_bh(&ifp->idev->lock);
+
+       /* While dad is in progress mld report's source address is in6_addrany.
+        * Resend with proper ll now.
+        */
+       if (send_mld)
+               ipv6_mc_dad_complete(ifp->idev);
+
+       if (send_rs) {
                /*
                 *      If a host as already performed a random delay
                 *      [...] as part of DAD [...] there is no need
                 *      to delay again before sending the first RS
                 */
-               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+               if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
+                       return;
+               ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters);
 
-               spin_lock_bh(&ifp->lock);
-               ifp->probes = 1;
+               write_lock_bh(&ifp->idev->lock);
+               spin_lock(&ifp->lock);
+               ifp->idev->rs_probes = 1;
                ifp->idev->if_flags |= IF_RS_SENT;
-               addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
-               spin_unlock_bh(&ifp->lock);
+               addrconf_mod_rs_timer(ifp->idev,
+                                     ifp->idev->cnf.rtr_solicit_interval);
+               spin_unlock(&ifp->lock);
+               write_unlock_bh(&ifp->idev->lock);
        }
 }
 
@@ -4351,8 +4374,11 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
 
        write_lock_bh(&idev->lock);
 
-       if (update_rs)
+       if (update_rs) {
                idev->if_flags |= IF_RS_SENT;
+               idev->rs_probes = 1;
+               addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval);
+       }
 
        /* Well, that's kinda nasty ... */
        list_for_each_entry(ifp, &idev->addr_list, if_list) {
@@ -4365,6 +4391,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
        }
 
        write_unlock_bh(&idev->lock);
+       addrconf_verify(0);
        return 0;
 }
 
@@ -4562,6 +4589,19 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);
 }
 
+static void update_valid_ll_addr_cnt(struct inet6_ifaddr *ifp, int count)
+{
+       write_lock_bh(&ifp->idev->lock);
+       spin_lock(&ifp->lock);
+       if (((ifp->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|
+                           IFA_F_DADFAILED)) == IFA_F_PERMANENT) &&
+           (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL))
+               ifp->idev->valid_ll_addr_cnt += count;
+       WARN_ON(ifp->idev->valid_ll_addr_cnt < 0);
+       spin_unlock(&ifp->lock);
+       write_unlock_bh(&ifp->idev->lock);
+}
+
 static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
        struct net *net = dev_net(ifp->idev->dev);
@@ -4570,6 +4610,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 
        switch (event) {
        case RTM_NEWADDR:
+               update_valid_ll_addr_cnt(ifp, 1);
+
                /*
                 * If the address was optimistic
                 * we inserted the route at the start of
@@ -4585,6 +4627,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                                              ifp->idev->dev, 0, 0);
                break;
        case RTM_DELADDR:
+               update_valid_ll_addr_cnt(ifp, -1);
+
                if (ifp->idev->cnf.forwarding)
                        addrconf_leave_anycast(ifp);
                addrconf_leave_solict(ifp->idev, &ifp->addr);
index 7210456..d2f8742 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/export.h>
 #include <net/ipv6.h>
+#include <net/addrconf.h>
 
 #define IPV6_ADDR_SCOPE_TYPE(scope)    ((scope) << 16)
 
index 72c8bfe..502c877 100644 (file)
@@ -999,6 +999,14 @@ static void mld_ifc_start_timer(struct inet6_dev *idev, int delay)
                in6_dev_hold(idev);
 }
 
+static void mld_dad_start_timer(struct inet6_dev *idev, int delay)
+{
+       int tv = net_random() % delay;
+
+       if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2))
+               in6_dev_hold(idev);
+}
+
 /*
  *     IGMP handling (alias multicast ICMPv6 messages)
  */
@@ -1815,6 +1823,46 @@ err_out:
        goto out;
 }
 
+static void mld_resend_report(struct inet6_dev *idev)
+{
+       if (MLD_V1_SEEN(idev)) {
+               struct ifmcaddr6 *mcaddr;
+               read_lock_bh(&idev->lock);
+               for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) {
+                       if (!(mcaddr->mca_flags & MAF_NOREPORT))
+                               igmp6_send(&mcaddr->mca_addr, idev->dev,
+                                          ICMPV6_MGM_REPORT);
+               }
+               read_unlock_bh(&idev->lock);
+       } else {
+               mld_send_report(idev, NULL);
+       }
+}
+
+void ipv6_mc_dad_complete(struct inet6_dev *idev)
+{
+       idev->mc_dad_count = idev->mc_qrv;
+       if (idev->mc_dad_count) {
+               mld_resend_report(idev);
+               idev->mc_dad_count--;
+               if (idev->mc_dad_count)
+                       mld_dad_start_timer(idev, idev->mc_maxdelay);
+       }
+}
+
+static void mld_dad_timer_expire(unsigned long data)
+{
+       struct inet6_dev *idev = (struct inet6_dev *)data;
+
+       mld_resend_report(idev);
+       if (idev->mc_dad_count) {
+               idev->mc_dad_count--;
+               if (idev->mc_dad_count)
+                       mld_dad_start_timer(idev, idev->mc_maxdelay);
+       }
+       __in6_dev_put(idev);
+}
+
 static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
        const struct in6_addr *psfsrc)
 {
@@ -2232,6 +2280,8 @@ void ipv6_mc_down(struct inet6_dev *idev)
        idev->mc_gq_running = 0;
        if (del_timer(&idev->mc_gq_timer))
                __in6_dev_put(idev);
+       if (del_timer(&idev->mc_dad_timer))
+               __in6_dev_put(idev);
 
        for (i = idev->mc_list; i; i=i->next)
                igmp6_group_dropped(i);
@@ -2268,6 +2318,8 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
        idev->mc_ifc_count = 0;
        setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire,
                        (unsigned long)idev);
+       setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire,
+                   (unsigned long)idev);
        idev->mc_qrv = MLD_QRV_DEFAULT;
        idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
        idev->mc_v1_seen = 0;
index f639866..97a0bfe 100644 (file)
@@ -466,14 +466,14 @@ isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t)
 
 static void ipip6_tunnel_uninit(struct net_device *dev)
 {
-       struct net *net = dev_net(dev);
-       struct sit_net *sitn = net_generic(net, sit_net_id);
+       struct ip_tunnel *tunnel = netdev_priv(dev);
+       struct sit_net *sitn = net_generic(tunnel->net, sit_net_id);
 
        if (dev == sitn->fb_tunnel_dev) {
                RCU_INIT_POINTER(sitn->tunnels_wc[0], NULL);
        } else {
-               ipip6_tunnel_unlink(sitn, netdev_priv(dev));
-               ipip6_tunnel_del_prl(netdev_priv(dev), NULL);
+               ipip6_tunnel_unlink(sitn, tunnel);
+               ipip6_tunnel_del_prl(tunnel, NULL);
        }
        dev_put(dev);
 }
@@ -621,6 +621,8 @@ static int ipip6_rcv(struct sk_buff *skb)
                tstats->rx_packets++;
                tstats->rx_bytes += skb->len;
 
+               if (tunnel->net != dev_net(tunnel->dev))
+                       skb_scrub_packet(skb);
                netif_rx(skb);
 
                return 0;
@@ -803,7 +805,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        goto tx_error;
        }
 
-       rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
+       rt = ip_route_output_ports(tunnel->net, &fl4, NULL,
                                   dst, tiph->saddr,
                                   0, 0,
                                   IPPROTO_IPV6, RT_TOS(tos),
@@ -858,6 +860,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
                        tunnel->err_count = 0;
        }
 
+       if (tunnel->net != dev_net(dev))
+               skb_scrub_packet(skb);
+
        /*
         * Okay, now see if we can stuff it in the buffer as-is.
         */
@@ -944,7 +949,8 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
        iph = &tunnel->parms.iph;
 
        if (iph->daddr) {
-               struct rtable *rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
+               struct rtable *rt = ip_route_output_ports(tunnel->net, &fl4,
+                                                         NULL,
                                                          iph->daddr, iph->saddr,
                                                          0, 0,
                                                          IPPROTO_IPV6,
@@ -959,7 +965,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
        }
 
        if (!tdev && tunnel->parms.link)
-               tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
+               tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link);
 
        if (tdev) {
                dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
@@ -972,7 +978,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
 
 static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
 {
-       struct net *net = dev_net(t->dev);
+       struct net *net = t->net;
        struct sit_net *sitn = net_generic(net, sit_net_id);
 
        ipip6_tunnel_unlink(sitn, t);
@@ -1248,7 +1254,6 @@ static void ipip6_tunnel_setup(struct net_device *dev)
        dev->priv_flags        &= ~IFF_XMIT_DST_RELEASE;
        dev->iflink             = 0;
        dev->addr_len           = 4;
-       dev->features           |= NETIF_F_NETNS_LOCAL;
        dev->features           |= NETIF_F_LLTX;
 }
 
@@ -1257,6 +1262,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
        struct ip_tunnel *tunnel = netdev_priv(dev);
 
        tunnel->dev = dev;
+       tunnel->net = dev_net(dev);
 
        memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
        memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
@@ -1277,6 +1283,7 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
        struct sit_net *sitn = net_generic(net, sit_net_id);
 
        tunnel->dev = dev;
+       tunnel->net = dev_net(dev);
        strcpy(tunnel->parms.name, dev->name);
 
        iph->version            = 4;
@@ -1564,8 +1571,14 @@ static struct xfrm_tunnel ipip_handler __read_mostly = {
 
 static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_head *head)
 {
+       struct net *net = dev_net(sitn->fb_tunnel_dev);
+       struct net_device *dev, *aux;
        int prio;
 
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &sit_link_ops)
+                       unregister_netdevice_queue(dev, head);
+
        for (prio = 1; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
@@ -1573,7 +1586,12 @@ static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_hea
 
                        t = rtnl_dereference(sitn->tunnels[prio][h]);
                        while (t != NULL) {
-                               unregister_netdevice_queue(t->dev, head);
+                               /* If dev is in the same netns, it has already
+                                * been added to the list by the previous loop.
+                                */
+                               if (dev_net(t->dev) != net)
+                                       unregister_netdevice_queue(t->dev,
+                                                                  head);
                                t = rtnl_dereference(t->next);
                        }
                }
@@ -1598,6 +1616,10 @@ static int __net_init sit_init_net(struct net *net)
                goto err_alloc_dev;
        }
        dev_net_set(sitn->fb_tunnel_dev, net);
+       /* FB netdevice is special: we have one, and only one per netns.
+        * Allowing to move it to another netns is clearly unsafe.
+        */
+       sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
 
        err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
        if (err)
index a1c6e1c..082f270 100644 (file)
@@ -1765,6 +1765,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
        /* mcast rate setting in Mesh Node */
        memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
                                                sizeof(setup->mcast_rate));
+       sdata->vif.bss_conf.basic_rates = setup->basic_rates;
 
        sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
        sdata->vif.bss_conf.dtim_period = setup->dtim_period;
@@ -1877,6 +1878,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
        if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
                conf->dot11MeshAwakeWindowDuration =
                        nconf->dot11MeshAwakeWindowDuration;
+       if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
+               conf->plink_timeout = nconf->plink_timeout;
        ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
 }
@@ -2844,6 +2847,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                return -EOPNOTSUPP;
        }
 
+       /* configurations requiring offchan cannot work if no channel has been
+        * specified
+        */
+       if (need_offchan && !chan)
+               return -EINVAL;
+
        mutex_lock(&local->mtx);
 
        /* Check if the operating channel is the requested channel */
@@ -2853,10 +2862,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                rcu_read_lock();
                chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 
-               if (chanctx_conf)
-                       need_offchan = chan != chanctx_conf->def.chan;
-               else
+               if (chanctx_conf) {
+                       need_offchan = chan && (chan != chanctx_conf->def.chan);
+               } else if (!chan) {
+                       ret = -EINVAL;
+                       rcu_read_unlock();
+                       goto out_unlock;
+               } else {
                        need_offchan = true;
+               }
                rcu_read_unlock();
        }
 
index 923e177..f97cd9d 100644 (file)
@@ -366,7 +366,7 @@ struct ieee80211_mgd_assoc_data {
        u8 ssid_len;
        u8 supp_rates_len;
        bool wmm, uapsd;
-       bool have_beacon, need_beacon;
+       bool need_beacon;
        bool synced;
        bool timeout_started;
 
@@ -404,6 +404,7 @@ struct ieee80211_if_managed {
 
        bool powersave; /* powersave requested for this iface */
        bool broken_ap; /* AP is broken -- turn off powersave */
+       bool have_beacon;
        u8 dtim_period;
        enum ieee80211_smps_mode req_smps, /* requested smps mode */
                                 driver_smps_mode; /* smps mode request */
index 1998f14..626c83c 100644 (file)
@@ -686,8 +686,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                return -EINVAL;
 
 #ifdef CONFIG_PM
-       if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) &&
-           (!local->ops->suspend || !local->ops->resume))
+       if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
                return -EINVAL;
 #endif
 
index b3d1fdd..6c33af4 100644 (file)
@@ -274,8 +274,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
        *pos++ = ifmsh->mesh_auth_id;
        /* Mesh Formation Info - number of neighbors */
        neighbors = atomic_read(&ifmsh->estab_plinks);
-       /* Number of neighbor mesh STAs or 15 whichever is smaller */
-       neighbors = (neighbors > 15) ? 15 : neighbors;
+       neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
        *pos++ = neighbors << 1;
        /* Mesh capability */
        *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
@@ -576,13 +575,11 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        u32 changed;
 
-       ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
+       ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);
        mesh_path_expire(sdata);
 
        changed = mesh_accept_plinks_update(sdata);
-       sdata_lock(sdata);
        ieee80211_mbss_info_change_notify(sdata, changed);
-       sdata_unlock(sdata);
 
        mod_timer(&ifmsh->housekeeping_timer,
                  round_jiffies(jiffies +
@@ -741,9 +738,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
                      BSS_CHANGED_HT |
                      BSS_CHANGED_BASIC_RATES |
                      BSS_CHANGED_BEACON_INT;
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_supported_band *sband =
-                                       sdata->local->hw.wiphy->bands[band];
 
        local->fif_other_bss++;
        /* mesh ifaces must set allmulti to forward mcast traffic */
@@ -761,7 +755,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
        sdata->vif.bss_conf.ht_operation_mode =
                                ifmsh->mshcfg.ht_opmode;
        sdata->vif.bss_conf.enable_beacon = true;
-       sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sband);
 
        changed |= ieee80211_mps_local_status_update(sdata);
 
@@ -789,12 +782,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        sdata->vif.bss_conf.enable_beacon = false;
        clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
-       sdata_lock(sdata);
        bcn = rcu_dereference_protected(ifmsh->beacon,
                                        lockdep_is_held(&sdata->wdev.mtx));
        rcu_assign_pointer(ifmsh->beacon, NULL);
        kfree_rcu(bcn, rcu_head);
-       sdata_unlock(sdata);
 
        /* flush STAs and mpaths on this iface */
        sta_info_flush(sdata);
@@ -807,14 +798,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        del_timer_sync(&sdata->u.mesh.housekeeping_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
        del_timer_sync(&sdata->u.mesh.mesh_path_timer);
-       /*
-        * If the timer fired while we waited for it, it will have
-        * requeued the work. Now the work will be running again
-        * but will not rearm the timer again because it checks
-        * whether the interface is running, which, at this point,
-        * it no longer is.
-        */
-       cancel_work_sync(&sdata->work);
 
        local->fif_other_bss--;
        atomic_dec(&local->iff_allmultis);
@@ -955,6 +938,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_mgmt *mgmt;
        u16 stype;
 
+       sdata_lock(sdata);
+
+       /* mesh already went down */
+       if (!sdata->wdev.mesh_id_len)
+               goto out;
+
        rx_status = IEEE80211_SKB_RXCB(skb);
        mgmt = (struct ieee80211_mgmt *) skb->data;
        stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
@@ -972,12 +961,20 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);
                break;
        }
+out:
+       sdata_unlock(sdata);
 }
 
 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
+       sdata_lock(sdata);
+
+       /* mesh already went down */
+       if (!sdata->wdev.mesh_id_len)
+               goto out;
+
        if (ifmsh->preq_queue_len &&
            time_after(jiffies,
                       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
@@ -997,6 +994,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
 
        if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
                mesh_sync_adjust_tbtt(sdata);
+
+out:
+       sdata_unlock(sdata);
 }
 
 void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
index da15877..01a28bc 100644 (file)
@@ -188,7 +188,6 @@ struct mesh_rmc {
        u32 idx_mask;
 };
 
-#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
 #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
 
 #define MESH_PATH_EXPIRE (600 * HZ)
@@ -324,14 +323,14 @@ static inline
 u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
 {
        atomic_inc(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
+       return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON;
 }
 
 static inline
 u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
 {
        atomic_dec(&sdata->u.mesh.estab_plinks);
-       return mesh_accept_plinks_update(sdata);
+       return mesh_accept_plinks_update(sdata) | BSS_CHANGED_BEACON;
 }
 
 static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
index 6c4da99..09bebed 100644 (file)
@@ -517,9 +517,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
        ieee80211_mps_frame_release(sta, elems);
 out:
        rcu_read_unlock();
-       sdata_lock(sdata);
        ieee80211_mbss_info_change_notify(sdata, changed);
-       sdata_unlock(sdata);
 }
 
 static void mesh_plink_timer(unsigned long data)
@@ -1070,9 +1068,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_unlock();
 
-       if (changed) {
-               sdata_lock(sdata);
+       if (changed)
                ieee80211_mbss_info_change_notify(sdata, changed);
-               sdata_unlock(sdata);
-       }
 }
index 118540b..9e49f55 100644 (file)
@@ -880,6 +880,10 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                                        IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+
+       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+
        if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
@@ -1356,7 +1360,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
                          IEEE80211_STA_CONNECTION_POLL))
                return false;
 
-       if (!sdata->vif.bss_conf.dtim_period)
+       if (!mgd->have_beacon)
                return false;
 
        rcu_read_lock();
@@ -1767,7 +1771,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_led_assoc(local, 1);
 
-       if (sdata->u.mgd.assoc_data->have_beacon) {
+       if (sdata->u.mgd.have_beacon) {
                /*
                 * If the AP is buggy we may get here with no DTIM period
                 * known, so assume it's 1 which is the only safe assumption
@@ -1775,7 +1779,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                 * probably just won't work at all.
                 */
                bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
-               bss_info_changed |= BSS_CHANGED_DTIM_PERIOD;
+               bss_info_changed |= BSS_CHANGED_BEACON_INFO;
        } else {
                bss_conf->dtim_period = 0;
        }
@@ -1899,6 +1903,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&sdata->u.mgd.chswitch_timer);
 
        sdata->vif.bss_conf.dtim_period = 0;
+       ifmgd->have_beacon = false;
 
        ifmgd->flags = 0;
        ieee80211_vif_release_channel(sdata);
@@ -2151,7 +2156,8 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
        sdata_unlock(sdata);
 }
 
@@ -2298,7 +2304,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                sdata_info(sdata, "%pM denied authentication (status %d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_auth_data(sdata, false);
-               cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
                return;
        }
 
@@ -2333,7 +2339,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                 * Report auth frame to user space for processing since another
                 * round of Authentication frames is still needed.
                 */
-               cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
                return;
        }
 
@@ -2350,7 +2356,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
        }
        mutex_unlock(&sdata->local->sta_mtx);
 
-       cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
        return;
  out_err:
        mutex_unlock(&sdata->local->sta_mtx);
@@ -2383,7 +2389,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, len);
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 }
 
 
@@ -2409,7 +2415,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, len);
+       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
 }
 
 static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2780,7 +2786,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                        /* oops -- internal error -- send timeout for now */
                        ieee80211_destroy_assoc_data(sdata, false);
                        cfg80211_put_bss(sdata->local->hw.wiphy, bss);
-                       cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
+                       cfg80211_assoc_timeout(sdata->dev, mgmt->bssid);
                        return;
                }
                sdata_info(sdata, "associated\n");
@@ -2793,7 +2799,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                ieee80211_destroy_assoc_data(sdata, true);
        }
 
-       cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, len);
+       cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -2805,24 +2811,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        int freq;
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
-       bool need_ps = false;
 
        sdata_assert_lock(sdata);
 
-       if ((sdata->u.mgd.associated &&
-            ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
-           (sdata->u.mgd.assoc_data &&
-            ether_addr_equal(mgmt->bssid,
-                             sdata->u.mgd.assoc_data->bss->bssid))) {
-               /* not previously set so we may need to recalc */
-               need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
-
-               if (elems->tim && !elems->parse_error) {
-                       const struct ieee80211_tim_ie *tim_ie = elems->tim;
-                       sdata->u.mgd.dtim_period = tim_ie->dtim_period;
-               }
-       }
-
        if (elems->ds_params)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0],
                                                      rx_status->band);
@@ -2843,12 +2834,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
            !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))
                return;
 
-       if (need_ps) {
-               mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local, -1);
-               mutex_unlock(&local->iflist_mtx);
-       }
-
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
                                         elems, true);
 
@@ -2962,7 +2947,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                       len - baselen, false, &elems);
 
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
-               ifmgd->assoc_data->have_beacon = true;
+               if (elems.tim && !elems.parse_error) {
+                       const struct ieee80211_tim_ie *tim_ie = elems.tim;
+                       ifmgd->dtim_period = tim_ie->dtim_period;
+               }
+               ifmgd->have_beacon = true;
                ifmgd->assoc_data->need_beacon = false;
                if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
                        sdata->vif.bss_conf.sync_tsf =
@@ -3144,7 +3133,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         * If we haven't had a beacon before, tell the driver about the
         * DTIM period (and beacon timing if desired) now.
         */
-       if (!bss_conf->dtim_period) {
+       if (!ifmgd->have_beacon) {
                /* a few bogus AP send dtim_period = 0 or no TIM IE */
                if (elems.tim)
                        bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
@@ -3163,7 +3152,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                sdata->vif.bss_conf.sync_dtim_count = 0;
                }
 
-               changed |= BSS_CHANGED_DTIM_PERIOD;
+               changed |= BSS_CHANGED_BEACON_INFO;
+               ifmgd->have_beacon = true;
+
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_recalc_ps(local, -1);
+               mutex_unlock(&local->iflist_mtx);
+
                ieee80211_recalc_ps_vif(sdata);
        }
 
@@ -3186,8 +3181,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DEAUTH_LEAVING,
                                       true, deauth_buf);
-               cfg80211_send_deauth(sdata->dev, deauth_buf,
-                                    sizeof(deauth_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
+                                     sizeof(deauth_buf));
                return;
        }
 
@@ -3305,7 +3300,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               tx, frame_buf);
 
-       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
 }
 
 static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3496,15 +3492,14 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 
                        ieee80211_destroy_auth_data(sdata, false);
 
-                       cfg80211_send_auth_timeout(sdata->dev, bssid);
+                       cfg80211_auth_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
                run_again(sdata, ifmgd->auth_data->timeout);
 
        if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
            time_after(jiffies, ifmgd->assoc_data->timeout)) {
-               if ((ifmgd->assoc_data->need_beacon &&
-                    !ifmgd->assoc_data->have_beacon) ||
+               if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
                    ieee80211_do_assoc(sdata)) {
                        u8 bssid[ETH_ALEN];
 
@@ -3512,7 +3507,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 
                        ieee80211_destroy_assoc_data(sdata, false);
 
-                       cfg80211_send_assoc_timeout(sdata->dev, bssid);
+                       cfg80211_assoc_timeout(sdata->dev, bssid);
                }
        } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
                run_again(sdata, ifmgd->assoc_data->timeout);
@@ -4061,8 +4056,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               cfg80211_send_deauth(sdata->dev, frame_buf,
-                                    sizeof(frame_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     sizeof(frame_buf));
        }
 
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4124,8 +4119,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               cfg80211_send_deauth(sdata->dev, frame_buf,
-                                    sizeof(frame_buf));
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     sizeof(frame_buf));
        }
 
        if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4272,6 +4267,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        ifmgd->assoc_data = assoc_data;
        ifmgd->dtim_period = 0;
+       ifmgd->have_beacon = false;
 
        err = ieee80211_prep_connection(sdata, req->bss, true);
        if (err)
@@ -4303,7 +4299,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        ifmgd->dtim_period = tim->dtim_period;
                        dtim_count = tim->dtim_count;
                }
-               assoc_data->have_beacon = true;
+               ifmgd->have_beacon = true;
                assoc_data->timeout = jiffies;
                assoc_data->timeout_started = true;
 
@@ -4378,8 +4374,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
  out:
        if (report_frame)
-               cfg80211_send_deauth(sdata->dev, frame_buf,
-                                    IEEE80211_DEAUTH_FRAME_LEN);
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     IEEE80211_DEAUTH_FRAME_LEN);
 
        return 0;
 }
@@ -4409,8 +4405,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                               req->reason_code, !req->local_state_change,
                               frame_buf);
 
-       cfg80211_send_disassoc(sdata->dev, frame_buf,
-                              IEEE80211_DEAUTH_FRAME_LEN);
+       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                             IEEE80211_DEAUTH_FRAME_LEN);
 
        return 0;
 }
index bdd7b4a..23dbcfc 100644 (file)
@@ -1747,27 +1747,21 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
                if (unlikely(!ieee80211_has_protected(fc) &&
                             ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
                             rx->key)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /* BIP does not use Protected field, so need to check MMIE */
                if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
                             ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /*
index a04c567..b429798 100644 (file)
@@ -1132,6 +1132,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
         * ends the poll/service period.
         */
        info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+                      IEEE80211_TX_CTL_PS_RESPONSE |
                       IEEE80211_TX_STATUS_EOSP |
                       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
@@ -1269,7 +1270,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                         * STA may still remain is PS mode after this frame
                         * exchange.
                         */
-                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
+                                      IEEE80211_TX_CTL_PS_RESPONSE;
 
                        /*
                         * Use MoreData flag to indicate whether there are
index 41c28b9..bd12fc5 100644 (file)
@@ -297,6 +297,9 @@ struct sta_ampdu_mlme {
  * @rcu_head: RCU head used for freeing this station struct
  * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
  *     taken from HT/VHT capabilities or VHT operating mode notification
+ * @chains: chains ever used for RX from this station
+ * @chain_signal_last: last signal (per chain)
+ * @chain_signal_avg: signal average (per chain)
  */
 struct sta_info {
        /* General information, mostly static */
index 34be933..4105d0c 100644 (file)
@@ -1790,12 +1790,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
-               if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
-                       /* Do not send frames with mesh_ttl == 0 */
-                       sdata->u.mesh.mshstats.dropped_frames_ttl++;
-                       goto fail_rcu;
-               }
-
                if (!is_multicast_ether_addr(skb->data)) {
                        struct sta_info *next_hop;
                        bool mpp_lookup = true;
index c75d3db..2265445 100644 (file)
@@ -1584,8 +1584,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                   BSS_CHANGED_ARP_FILTER |
                                   BSS_CHANGED_PS;
 
-                       if (sdata->u.mgd.dtim_period)
-                               changed |= BSS_CHANGED_DTIM_PERIOD;
+                       /* Re-send beacon info report to the driver */
+                       if (sdata->u.mgd.have_beacon)
+                               changed |= BSS_CHANGED_BEACON_INFO;
 
                        sdata_lock(sdata);
                        ieee80211_bss_info_change_notify(sdata, changed);
index 275d901..0c61b59 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/audit.h>
 #include <linux/mutex.h>
 #include <linux/vmalloc.h>
+#include <linux/if_arp.h>
 #include <asm/cacheflush.h>
 
 #include <net/net_namespace.h>
@@ -101,6 +102,9 @@ static atomic_t nl_table_users = ATOMIC_INIT(0);
 
 static ATOMIC_NOTIFIER_HEAD(netlink_chain);
 
+static DEFINE_SPINLOCK(netlink_tap_lock);
+static struct list_head netlink_tap_all __read_mostly;
+
 static inline u32 netlink_group_mask(u32 group)
 {
        return group ? 1 << (group - 1) : 0;
@@ -111,6 +115,100 @@ static inline struct hlist_head *nl_portid_hashfn(struct nl_portid_hash *hash, u
        return &hash->table[jhash_1word(portid, hash->rnd) & hash->mask];
 }
 
+int netlink_add_tap(struct netlink_tap *nt)
+{
+       if (unlikely(nt->dev->type != ARPHRD_NETLINK))
+               return -EINVAL;
+
+       spin_lock(&netlink_tap_lock);
+       list_add_rcu(&nt->list, &netlink_tap_all);
+       spin_unlock(&netlink_tap_lock);
+
+       if (nt->module)
+               __module_get(nt->module);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(netlink_add_tap);
+
+int __netlink_remove_tap(struct netlink_tap *nt)
+{
+       bool found = false;
+       struct netlink_tap *tmp;
+
+       spin_lock(&netlink_tap_lock);
+
+       list_for_each_entry(tmp, &netlink_tap_all, list) {
+               if (nt == tmp) {
+                       list_del_rcu(&nt->list);
+                       found = true;
+                       goto out;
+               }
+       }
+
+       pr_warn("__netlink_remove_tap: %p not found\n", nt);
+out:
+       spin_unlock(&netlink_tap_lock);
+
+       if (found && nt->module)
+               module_put(nt->module);
+
+       return found ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_GPL(__netlink_remove_tap);
+
+int netlink_remove_tap(struct netlink_tap *nt)
+{
+       int ret;
+
+       ret = __netlink_remove_tap(nt);
+       synchronize_net();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(netlink_remove_tap);
+
+static int __netlink_deliver_tap_skb(struct sk_buff *skb,
+                                    struct net_device *dev)
+{
+       struct sk_buff *nskb;
+       int ret = -ENOMEM;
+
+       dev_hold(dev);
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (nskb) {
+               nskb->dev = dev;
+               ret = dev_queue_xmit(nskb);
+               if (unlikely(ret > 0))
+                       ret = net_xmit_errno(ret);
+       }
+
+       dev_put(dev);
+       return ret;
+}
+
+static void __netlink_deliver_tap(struct sk_buff *skb)
+{
+       int ret;
+       struct netlink_tap *tmp;
+
+       list_for_each_entry_rcu(tmp, &netlink_tap_all, list) {
+               ret = __netlink_deliver_tap_skb(skb, tmp->dev);
+               if (unlikely(ret))
+                       break;
+       }
+}
+
+static void netlink_deliver_tap(struct sk_buff *skb)
+{
+       rcu_read_lock();
+
+       if (unlikely(!list_empty(&netlink_tap_all)))
+               __netlink_deliver_tap(skb);
+
+       rcu_read_unlock();
+}
+
 static void netlink_overrun(struct sock *sk)
 {
        struct netlink_sock *nlk = nlk_sk(sk);
@@ -751,7 +849,10 @@ static void netlink_skb_destructor(struct sk_buff *skb)
        }
 #endif
        if (is_vmalloc_addr(skb->head)) {
-               vfree(skb->head);
+               if (!skb->cloned ||
+                   !atomic_dec_return(&(skb_shinfo(skb)->dataref)))
+                       vfree(skb->head);
+
                skb->head = NULL;
        }
        if (skb->sk != NULL)
@@ -1434,33 +1535,31 @@ struct sock *netlink_getsockbyfilp(struct file *filp)
        return sock;
 }
 
-static struct sk_buff *netlink_alloc_large_skb(unsigned int size)
+static struct sk_buff *netlink_alloc_large_skb(unsigned int size,
+                                              int broadcast)
 {
        struct sk_buff *skb;
        void *data;
 
-       if (size <= NLMSG_GOODSIZE)
+       if (size <= NLMSG_GOODSIZE || broadcast)
                return alloc_skb(size, GFP_KERNEL);
 
-       skb = alloc_skb_head(GFP_KERNEL);
-       if (skb == NULL)
-               return NULL;
+       size = SKB_DATA_ALIGN(size) +
+              SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        data = vmalloc(size);
        if (data == NULL)
-               goto err;
+               return NULL;
 
-       skb->head       = data;
-       skb->data       = data;
-       skb_reset_tail_pointer(skb);
-       skb->end        = skb->tail + size;
-       skb->len        = 0;
-       skb->destructor = netlink_skb_destructor;
+       skb = build_skb(data, size);
+       if (skb == NULL)
+               vfree(data);
+       else {
+               skb->head_frag = 0;
+               skb->destructor = netlink_skb_destructor;
+       }
 
        return skb;
-err:
-       kfree_skb(skb);
-       return NULL;
 }
 
 /*
@@ -1518,6 +1617,8 @@ static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb)
 {
        int len = skb->len;
 
+       netlink_deliver_tap(skb);
+
 #ifdef CONFIG_NETLINK_MMAP
        if (netlink_skb_is_mmaped(skb))
                netlink_queue_mmaped_skb(sk, skb);
@@ -1578,6 +1679,11 @@ static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb,
 
        ret = -ECONNREFUSED;
        if (nlk->netlink_rcv != NULL) {
+               /* We could do a netlink_deliver_tap(skb) here as well
+                * but since this is intended for the kernel only, we
+                * should rather let it stay under the hood.
+                */
+
                ret = skb->len;
                netlink_skb_set_owner_r(skb, sk);
                NETLINK_CB(skb).sk = ssk;
@@ -2139,7 +2245,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
        if (len > sk->sk_sndbuf - 32)
                goto out;
        err = -ENOBUFS;
-       skb = netlink_alloc_large_skb(len);
+       skb = netlink_alloc_large_skb(len, dst_group);
        if (skb == NULL)
                goto out;
 
@@ -2975,6 +3081,8 @@ static int __init netlink_proto_init(void)
                nl_table[i].compare = netlink_compare;
        }
 
+       INIT_LIST_HEAD(&netlink_tap_all);
+
        netlink_add_usersock_entry();
 
        sock_register(&netlink_family_ops);
index 40d2527..dc96a83 100644 (file)
@@ -44,6 +44,47 @@ DEFINE_MUTEX(nfc_devlist_mutex);
 /* NFC device ID bitmap */
 static DEFINE_IDA(nfc_index_ida);
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name)
+{
+       int rc = 0;
+
+       pr_debug("%s do firmware %s\n", dev_name(&dev->dev), firmware_name);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->dev_up) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->fw_upload) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       dev->fw_upload_in_progress = true;
+       rc = dev->ops->fw_upload(dev, firmware_name);
+       if (rc)
+               dev->fw_upload_in_progress = false;
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       dev->fw_upload_in_progress = false;
+
+       return nfc_genl_fw_upload_done(dev, firmware_name);
+}
+EXPORT_SYMBOL(nfc_fw_upload_done);
+
 /**
  * nfc_dev_up - turn on the NFC device
  *
@@ -69,6 +110,11 @@ int nfc_dev_up(struct nfc_dev *dev)
                goto error;
        }
 
+       if (dev->fw_upload_in_progress) {
+               rc = -EBUSY;
+               goto error;
+       }
+
        if (dev->dev_up) {
                rc = -EALREADY;
                goto error;
@@ -80,6 +126,13 @@ int nfc_dev_up(struct nfc_dev *dev)
        if (!rc)
                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");
+       }
+
 error:
        device_unlock(&dev->dev);
        return rc;
@@ -475,6 +528,108 @@ error:
        return rc;
 }
 
+static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx)
+                       return se;
+
+       return NULL;
+}
+
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (dev->polling) {
+               rc = -EBUSY;
+               goto error;
+       }
+
+       if (!dev->ops->enable_se || !dev->ops->disable_se) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->type == NFC_SE_ENABLED) {
+               rc = -EALREADY;
+               goto error;
+       }
+
+       rc = dev->ops->enable_se(dev, se_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
+{
+
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       device_lock(&dev->dev);
+
+       if (!device_is_registered(&dev->dev)) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->dev_up) {
+               rc = -ENODEV;
+               goto error;
+       }
+
+       if (!dev->ops->enable_se || !dev->ops->disable_se) {
+               rc = -EOPNOTSUPP;
+               goto error;
+       }
+
+       se = find_se(dev, se_idx);
+       if (!se) {
+               rc = -EINVAL;
+               goto error;
+       }
+
+       if (se->type == NFC_SE_DISABLED) {
+               rc = -EALREADY;
+               goto error;
+       }
+
+       rc = dev->ops->disable_se(dev, se_idx);
+
+error:
+       device_unlock(&dev->dev);
+       return rc;
+}
+
 int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
 {
        pr_debug("dev_name=%s gb_len=%d\n", dev_name(&dev->dev), gb_len);
@@ -707,14 +862,79 @@ inline void nfc_driver_failure(struct nfc_dev *dev, int err)
 }
 EXPORT_SYMBOL(nfc_driver_failure);
 
+int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct nfc_se *se;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       se = find_se(dev, se_idx);
+       if (se)
+               return -EALREADY;
+
+       se = kzalloc(sizeof(struct nfc_se), GFP_KERNEL);
+       if (!se)
+               return -ENOMEM;
+
+       se->idx = se_idx;
+       se->type = type;
+       se->state = NFC_SE_DISABLED;
+       INIT_LIST_HEAD(&se->list);
+
+       list_add(&se->list, &dev->secure_elements);
+
+       rc = nfc_genl_se_added(dev, se_idx, type);
+       if (rc < 0) {
+               list_del(&se->list);
+               kfree(se);
+
+               return rc;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(nfc_add_se);
+
+int nfc_remove_se(struct nfc_dev *dev, u32 se_idx)
+{
+       struct nfc_se *se, *n;
+       int rc;
+
+       pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list)
+               if (se->idx == se_idx) {
+                       rc = nfc_genl_se_removed(dev, se_idx);
+                       if (rc < 0)
+                               return rc;
+
+                       list_del(&se->list);
+                       kfree(se);
+
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(nfc_remove_se);
+
 static void nfc_release(struct device *d)
 {
        struct nfc_dev *dev = to_nfc_dev(d);
+       struct nfc_se *se, *n;
 
        pr_debug("dev_name=%s\n", dev_name(&dev->dev));
 
        nfc_genl_data_exit(&dev->genl_data);
        kfree(dev->targets);
+
+       list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+                       nfc_genl_se_removed(dev, se->idx);
+                       list_del(&se->list);
+                       kfree(se);
+       }
+
        kfree(dev);
 }
 
@@ -786,7 +1006,6 @@ struct nfc_dev *nfc_get_device(unsigned int idx)
  */
 struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
                                    u32 supported_protocols,
-                                   u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nfc_dev *dev;
@@ -804,10 +1023,9 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
 
        dev->ops = ops;
        dev->supported_protocols = supported_protocols;
-       dev->supported_se = supported_se;
-       dev->active_se = NFC_SE_NONE;
        dev->tx_headroom = tx_headroom;
        dev->tx_tailroom = tx_tailroom;
+       INIT_LIST_HEAD(&dev->secure_elements);
 
        nfc_genl_data_init(&dev->genl_data);
 
index 91020b2..7b1c186 100644 (file)
@@ -570,21 +570,21 @@ static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->dep_link_up)
-               return hdev->ops->dep_link_up(hdev, target, comm_mode,
-                                               gb, gb_len);
+       if (!hdev->ops->dep_link_up)
+               return 0;
 
-       return 0;
+       return hdev->ops->dep_link_up(hdev, target, comm_mode,
+                                     gb, gb_len);
 }
 
 static int hci_dep_link_down(struct nfc_dev *nfc_dev)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->dep_link_down)
-               return hdev->ops->dep_link_down(hdev);
+       if (!hdev->ops->dep_link_down)
+               return 0;
 
-       return 0;
+       return hdev->ops->dep_link_down(hdev);
 }
 
 static int hci_activate_target(struct nfc_dev *nfc_dev,
@@ -673,12 +673,12 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->tm_send)
-               return hdev->ops->tm_send(hdev, skb);
-
-       kfree_skb(skb);
+       if (!hdev->ops->tm_send) {
+               kfree_skb(skb);
+               return -ENOTSUPP;
+       }
 
-       return -ENOTSUPP;
+       return hdev->ops->tm_send(hdev, skb);
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -686,8 +686,38 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
-       if (hdev->ops->check_presence)
-               return hdev->ops->check_presence(hdev, target);
+       if (!hdev->ops->check_presence)
+               return 0;
+
+       return hdev->ops->check_presence(hdev, target);
+}
+
+static int hci_discover_se(struct nfc_dev *nfc_dev)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->discover_se)
+               return hdev->ops->discover_se(hdev);
+
+       return 0;
+}
+
+static int hci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->enable_se)
+               return hdev->ops->enable_se(hdev, se_idx);
+
+       return 0;
+}
+
+static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (hdev->ops->disable_se)
+               return hdev->ops->enable_se(hdev, se_idx);
 
        return 0;
 }
@@ -779,6 +809,16 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
+static int hci_fw_upload(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+       struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+       if (!hdev->ops->fw_upload)
+               return -ENOTSUPP;
+
+       return hdev->ops->fw_upload(hdev, firmware_name);
+}
+
 static struct nfc_ops hci_nfc_ops = {
        .dev_up = hci_dev_up,
        .dev_down = hci_dev_down,
@@ -791,13 +831,16 @@ static struct nfc_ops hci_nfc_ops = {
        .im_transceive = hci_transceive,
        .tm_send = hci_tm_send,
        .check_presence = hci_check_presence,
+       .fw_upload = hci_fw_upload,
+       .discover_se = hci_discover_se,
+       .enable_se = hci_enable_se,
+       .disable_se = hci_disable_se,
 };
 
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            unsigned long quirks,
                                            u32 protocols,
-                                           u32 supported_se,
                                            const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
@@ -823,7 +866,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                return NULL;
        }
 
-       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se,
+       hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
index ff8c434..f4d48b5 100644 (file)
@@ -19,6 +19,8 @@
 
 enum llcp_state {
        LLCP_CONNECTED = 1, /* wait_for_packet() wants that */
+       LLCP_CONNECTING,
+       LLCP_DISCONNECTING,
        LLCP_CLOSED,
        LLCP_BOUND,
        LLCP_LISTEN,
@@ -246,7 +248,6 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri,
 void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp);
 void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head);
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_symm(struct nfc_dev *dev);
 int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
 int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
index c1b23ee..1017894 100644 (file)
@@ -339,7 +339,7 @@ static struct sk_buff *llcp_allocate_pdu(struct nfc_llcp_sock *sock,
        return skb;
 }
 
-int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
+int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
 {
        struct sk_buff *skb;
        struct nfc_dev *dev;
@@ -630,26 +630,6 @@ int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
        return 0;
 }
 
-int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock)
-{
-       struct sk_buff *skb;
-       struct nfc_llcp_local *local;
-
-       pr_debug("Send DISC\n");
-
-       local = sock->local;
-       if (local == NULL)
-               return -ENODEV;
-
-       skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
-       if (skb == NULL)
-               return -ENOMEM;
-
-       skb_queue_head(&local->tx_queue, skb);
-
-       return 0;
-}
-
 int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
                          struct msghdr *msg, size_t len)
 {
index 158bdbf..81cd341 100644 (file)
@@ -537,6 +537,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        u8 *lto_tlv, lto_length;
        u8 *wks_tlv, wks_length;
        u8 *miux_tlv, miux_length;
+       __be16 wks = cpu_to_be16(local->local_wks);
        u8 gb_len = 0;
        int ret = 0;
 
@@ -549,8 +550,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        gb_len += lto_length;
 
        pr_debug("Local wks 0x%lx\n", local->local_wks);
-       wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&local->local_wks, 2,
-                                    &wks_length);
+       wks_tlv = nfc_llcp_build_tlv(LLCP_TLV_WKS, (u8 *)&wks, 2, &wks_length);
        gb_len += wks_length;
 
        miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
@@ -719,6 +719,10 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                llcp_sock = nfc_llcp_sock(sk);
 
                if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+                       kfree_skb(skb);
+                       nfc_llcp_send_symm(local->dev);
+               } else if (llcp_sock && !llcp_sock->remote_ready) {
+                       skb_queue_head(&local->tx_queue, skb);
                        nfc_llcp_send_symm(local->dev);
                } else {
                        struct sk_buff *copy_skb = NULL;
@@ -730,6 +734,13 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       if (ptype == LLCP_PDU_DISC && sk != NULL &&
+                           sk->sk_state == LLCP_DISCONNECTING) {
+                               nfc_llcp_sock_unlink(&local->sockets, sk);
+                               sock_orphan(sk);
+                               sock_put(sk);
+                       }
+
                        if (ptype == LLCP_PDU_I)
                                copy_skb = skb_copy(skb, GFP_ATOMIC);
 
@@ -1579,6 +1590,7 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
        local->lto = 150; /* 1500 ms */
        local->rw = LLCP_MAX_RW;
        local->miux = cpu_to_be16(LLCP_MAX_MIUX);
+       local->local_wks = 0x1; /* LLC Link Management */
 
        nfc_llcp_build_gb(local);
 
index 380253e..d308402 100644 (file)
@@ -571,7 +571,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
        if (sk->sk_shutdown == SHUTDOWN_MASK)
                mask |= POLLHUP;
 
-       if (sock_writeable(sk))
+       if (sock_writeable(sk) && sk->sk_state == LLCP_CONNECTED)
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
        else
                set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
@@ -603,7 +603,7 @@ static int llcp_sock_release(struct socket *sock)
 
        /* Send a DISC */
        if (sk->sk_state == LLCP_CONNECTED)
-               nfc_llcp_disconnect(llcp_sock);
+               nfc_llcp_send_disconnect(llcp_sock);
 
        if (sk->sk_state == LLCP_LISTEN) {
                struct nfc_llcp_sock *lsk, *n;
@@ -614,7 +614,7 @@ static int llcp_sock_release(struct socket *sock)
                        accept_sk = &lsk->sk;
                        lock_sock(accept_sk);
 
-                       nfc_llcp_disconnect(lsk);
+                       nfc_llcp_send_disconnect(lsk);
                        nfc_llcp_accept_unlink(accept_sk);
 
                        release_sock(accept_sk);
@@ -626,6 +626,13 @@ static int llcp_sock_release(struct socket *sock)
 
        release_sock(sk);
 
+       /* Keep this sock alive and therefore do not remove it from the sockets
+        * list until the DISC PDU has been actually sent. Otherwise we would
+        * reply with DM PDUs before sending the DISC one.
+        */
+       if (sk->sk_state == LLCP_DISCONNECTING)
+               return err;
+
        if (sock->type == SOCK_RAW)
                nfc_llcp_sock_unlink(&local->raw_sockets, sk);
        else
@@ -722,14 +729,16 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
        if (ret)
                goto sock_unlink;
 
+       sk->sk_state = LLCP_CONNECTING;
+
        ret = sock_wait_state(sk, LLCP_CONNECTED,
                              sock_sndtimeo(sk, flags & O_NONBLOCK));
-       if (ret)
+       if (ret && ret != -EINPROGRESS)
                goto sock_unlink;
 
        release_sock(sk);
 
-       return 0;
+       return ret;
 
 sock_unlink:
        nfc_llcp_put_ssap(local, llcp_sock->ssap);
index 6d69b5f..2a24160 100644 (file)
@@ -8,3 +8,13 @@ config NFC_NCI
 
          Say Y here to compile NCI support into the kernel or say M to
          compile it as module (nci).
+
+config NFC_NCI_SPI
+       depends on NFC_NCI && SPI
+       bool "NCI over SPI protocol support"
+       default n
+       help
+         NCI (NFC Controller Interface) is a communication protocol between
+         an NFC Controller (NFCC) and a Device Host (DH).
+
+         Say yes if you use an NCI driver that requires SPI link layer.
index cdb3a2e..7aeedc4 100644 (file)
@@ -4,4 +4,6 @@
 
 obj-$(CONFIG_NFC_NCI) += nci.o
 
-nci-objs := core.o data.o lib.o ntf.o rsp.o
\ No newline at end of file
+nci-objs := core.o data.o lib.o ntf.o rsp.o
+
+nci-$(CONFIG_NFC_NCI_SPI) += spi.o
index 48ada0e..b943d46 100644 (file)
@@ -636,6 +636,21 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
        return rc;
 }
 
+static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       return 0;
+}
+
+static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
+{
+       return 0;
+}
+
+static int nci_discover_se(struct nfc_dev *nfc_dev)
+{
+       return 0;
+}
+
 static struct nfc_ops nci_nfc_ops = {
        .dev_up = nci_dev_up,
        .dev_down = nci_dev_down,
@@ -646,6 +661,9 @@ static struct nfc_ops nci_nfc_ops = {
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
        .im_transceive = nci_transceive,
+       .enable_se = nci_enable_se,
+       .disable_se = nci_disable_se,
+       .discover_se = nci_discover_se,
 };
 
 /* ---- Interface to NCI drivers ---- */
@@ -658,7 +676,6 @@ static struct nfc_ops nci_nfc_ops = {
  */
 struct nci_dev *nci_allocate_device(struct nci_ops *ops,
                                    __u32 supported_protocols,
-                                   __u32 supported_se,
                                    int tx_headroom, int tx_tailroom)
 {
        struct nci_dev *ndev;
@@ -681,7 +698,6 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
 
        ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
                                            supported_protocols,
-                                           supported_se,
                                            tx_headroom + NCI_DATA_HDR_SIZE,
                                            tx_tailroom);
        if (!ndev->nfc_dev)
@@ -797,12 +813,11 @@ EXPORT_SYMBOL(nci_unregister_device);
 /**
  * nci_recv_frame - receive frame from NCI drivers
  *
+ * @ndev: The nci device
  * @skb: The sk_buff to receive
  */
-int nci_recv_frame(struct sk_buff *skb)
+int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
        pr_debug("len %d\n", skb->len);
 
        if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
@@ -819,10 +834,8 @@ int nci_recv_frame(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(nci_recv_frame);
 
-static int nci_send_frame(struct sk_buff *skb)
+static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
 {
-       struct nci_dev *ndev = (struct nci_dev *) skb->dev;
-
        pr_debug("len %d\n", skb->len);
 
        if (!ndev) {
@@ -833,7 +846,7 @@ static int nci_send_frame(struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
-       return ndev->ops->send(skb);
+       return ndev->ops->send(ndev, skb);
 }
 
 /* Send NCI command */
@@ -861,8 +874,6 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
        if (plen)
                memcpy(skb_put(skb, plen), payload, plen);
 
-       skb->dev = (void *) ndev;
-
        skb_queue_tail(&ndev->cmd_q, skb);
        queue_work(ndev->cmd_wq, &ndev->cmd_work);
 
@@ -894,7 +905,7 @@ static void nci_tx_work(struct work_struct *work)
                         nci_conn_id(skb->data),
                         nci_plen(skb->data));
 
-               nci_send_frame(skb);
+               nci_send_frame(ndev, skb);
 
                mod_timer(&ndev->data_timer,
                          jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
@@ -963,7 +974,7 @@ static void nci_cmd_work(struct work_struct *work)
                         nci_opcode_oid(nci_opcode(skb->data)),
                         nci_plen(skb->data));
 
-               nci_send_frame(skb);
+               nci_send_frame(ndev, skb);
 
                mod_timer(&ndev->cmd_timer,
                          jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
index 76c48c5..2a9399d 100644 (file)
@@ -80,8 +80,6 @@ static inline void nci_push_data_hdr(struct nci_dev *ndev,
 
        nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
        nci_pbf_set((__u8 *)hdr, pbf);
-
-       skb->dev = (void *) ndev;
 }
 
 static int nci_queue_tx_data_frags(struct nci_dev *ndev,
diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c
new file mode 100644 (file)
index 0000000..c7cf37b
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#define pr_fmt(fmt) "nci_spi: %s: " fmt, __func__
+
+#include <linux/export.h>
+#include <linux/spi/spi.h>
+#include <linux/crc-ccitt.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci_core.h>
+
+#define NCI_SPI_HDR_LEN                        4
+#define NCI_SPI_CRC_LEN                        2
+#define NCI_SPI_ACK_SHIFT              6
+#define NCI_SPI_MSB_PAYLOAD_MASK       0x3F
+
+#define NCI_SPI_SEND_TIMEOUT   (NCI_CMD_TIMEOUT > NCI_DATA_TIMEOUT ? \
+                                       NCI_CMD_TIMEOUT : NCI_DATA_TIMEOUT)
+
+#define NCI_SPI_DIRECT_WRITE   0x01
+#define NCI_SPI_DIRECT_READ    0x02
+
+#define ACKNOWLEDGE_NONE       0
+#define ACKNOWLEDGE_ACK                1
+#define ACKNOWLEDGE_NACK       2
+
+#define CRC_INIT               0xFFFF
+
+static int nci_spi_open(struct nci_dev *nci_dev)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+       return ndev->ops->open(ndev);
+}
+
+static int nci_spi_close(struct nci_dev *nci_dev)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+
+       return ndev->ops->close(ndev);
+}
+
+static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb)
+{
+       struct spi_message m;
+       struct spi_transfer t;
+
+       t.tx_buf = skb->data;
+       t.len = skb->len;
+       t.cs_change = 0;
+       t.delay_usecs = ndev->xfer_udelay;
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+
+       return spi_sync(ndev->spi, &m);
+}
+
+static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb)
+{
+       struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev);
+       unsigned int payload_len = skb->len;
+       unsigned char *hdr;
+       int ret;
+       long completion_rc;
+
+       ndev->ops->deassert_int(ndev);
+
+       /* add the NCI SPI header to the start of the buffer */
+       hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+       hdr[0] = NCI_SPI_DIRECT_WRITE;
+       hdr[1] = ndev->acknowledge_mode;
+       hdr[2] = payload_len >> 8;
+       hdr[3] = payload_len & 0xFF;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               u16 crc;
+
+               crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+               *skb_put(skb, 1) = crc >> 8;
+               *skb_put(skb, 1) = crc & 0xFF;
+       }
+
+       ret = __nci_spi_send(ndev, skb);
+
+       kfree_skb(skb);
+       ndev->ops->assert_int(ndev);
+
+       if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED)
+               goto done;
+
+       init_completion(&ndev->req_completion);
+       completion_rc =
+               wait_for_completion_interruptible_timeout(&ndev->req_completion,
+                                                         NCI_SPI_SEND_TIMEOUT);
+
+       if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK)
+               ret = -EIO;
+
+done:
+       return ret;
+}
+
+static struct nci_ops nci_spi_ops = {
+       .open = nci_spi_open,
+       .close = nci_spi_close,
+       .send = nci_spi_send,
+};
+
+/* ---- Interface to NCI SPI drivers ---- */
+
+/**
+ * nci_spi_allocate_device - allocate a new nci spi device
+ *
+ * @spi: SPI device
+ * @ops: device operations
+ * @supported_protocols: NFC protocols supported by the device
+ * @supported_se: NFC Secure Elements supported by the device
+ * @acknowledge_mode: Acknowledge mode used by the device
+ * @delay: delay between transactions in us
+ */
+struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi,
+                                               struct nci_spi_ops *ops,
+                                               u32 supported_protocols,
+                                               u32 supported_se,
+                                               u8 acknowledge_mode,
+                                               unsigned int delay)
+{
+       struct nci_spi_dev *ndev;
+       int tailroom = 0;
+
+       if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int)
+               return NULL;
+
+       if (!supported_protocols)
+               return NULL;
+
+       ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL);
+       if (!ndev)
+               return NULL;
+
+       ndev->ops = ops;
+       ndev->acknowledge_mode = acknowledge_mode;
+       ndev->xfer_udelay = delay;
+
+       if (acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               tailroom += NCI_SPI_CRC_LEN;
+
+       ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols,
+                                           NCI_SPI_HDR_LEN, tailroom);
+       if (!ndev->nci_dev)
+               return NULL;
+
+       nci_set_drvdata(ndev->nci_dev, ndev);
+
+       return ndev;
+}
+EXPORT_SYMBOL_GPL(nci_spi_allocate_device);
+
+/**
+ * nci_spi_free_device - deallocate nci spi device
+ *
+ * @ndev: The nci spi device to deallocate
+ */
+void nci_spi_free_device(struct nci_spi_dev *ndev)
+{
+       nci_free_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_free_device);
+
+/**
+ * nci_spi_register_device - register a nci spi device in the nfc subsystem
+ *
+ * @pdev: The nci spi device to register
+ */
+int nci_spi_register_device(struct nci_spi_dev *ndev)
+{
+       return nci_register_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_register_device);
+
+/**
+ * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem
+ *
+ * @dev: The nci spi device to unregister
+ */
+void nci_spi_unregister_device(struct nci_spi_dev *ndev)
+{
+       nci_unregister_device(ndev->nci_dev);
+}
+EXPORT_SYMBOL_GPL(nci_spi_unregister_device);
+
+static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge)
+{
+       struct sk_buff *skb;
+       unsigned char *hdr;
+       u16 crc;
+       int ret;
+
+       skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL);
+
+       /* add the NCI SPI header to the start of the buffer */
+       hdr = skb_push(skb, NCI_SPI_HDR_LEN);
+       hdr[0] = NCI_SPI_DIRECT_WRITE;
+       hdr[1] = NCI_SPI_CRC_ENABLED;
+       hdr[2] = acknowledge << NCI_SPI_ACK_SHIFT;
+       hdr[3] = 0;
+
+       crc = crc_ccitt(CRC_INIT, skb->data, skb->len);
+       *skb_put(skb, 1) = crc >> 8;
+       *skb_put(skb, 1) = crc & 0xFF;
+
+       ret = __nci_spi_send(ndev, skb);
+
+       kfree_skb(skb);
+
+       return ret;
+}
+
+static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+       struct sk_buff *skb;
+       struct spi_message m;
+       unsigned char req[2], resp_hdr[2];
+       struct spi_transfer tx, rx;
+       unsigned short rx_len = 0;
+       int ret;
+
+       spi_message_init(&m);
+       req[0] = NCI_SPI_DIRECT_READ;
+       req[1] = ndev->acknowledge_mode;
+       tx.tx_buf = req;
+       tx.len = 2;
+       tx.cs_change = 0;
+       spi_message_add_tail(&tx, &m);
+       rx.rx_buf = resp_hdr;
+       rx.len = 2;
+       rx.cs_change = 1;
+       spi_message_add_tail(&rx, &m);
+       ret = spi_sync(ndev->spi, &m);
+
+       if (ret)
+               return NULL;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) +
+                               resp_hdr[1] + NCI_SPI_CRC_LEN;
+       else
+               rx_len = (resp_hdr[0] << 8) | resp_hdr[1];
+
+       skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       spi_message_init(&m);
+       rx.rx_buf = skb_put(skb, rx_len);
+       rx.len = rx_len;
+       rx.cs_change = 0;
+       rx.delay_usecs = ndev->xfer_udelay;
+       spi_message_add_tail(&rx, &m);
+       ret = spi_sync(ndev->spi, &m);
+
+       if (ret)
+               goto receive_error;
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               *skb_push(skb, 1) = resp_hdr[1];
+               *skb_push(skb, 1) = resp_hdr[0];
+       }
+
+       return skb;
+
+receive_error:
+       kfree_skb(skb);
+
+       return NULL;
+}
+
+static int nci_spi_check_crc(struct sk_buff *skb)
+{
+       u16 crc_data = (skb->data[skb->len - 2] << 8) |
+                       skb->data[skb->len - 1];
+       int ret;
+
+       ret = (crc_ccitt(CRC_INIT, skb->data, skb->len - NCI_SPI_CRC_LEN)
+                       == crc_data);
+
+       skb_trim(skb, skb->len - NCI_SPI_CRC_LEN);
+
+       return ret;
+}
+
+static u8 nci_spi_get_ack(struct sk_buff *skb)
+{
+       u8 ret;
+
+       ret = skb->data[0] >> NCI_SPI_ACK_SHIFT;
+
+       /* Remove NFCC part of the header: ACK, NACK and MSB payload len */
+       skb_pull(skb, 2);
+
+       return ret;
+}
+
+/**
+ * nci_spi_recv_frame - receive frame from NCI SPI drivers
+ *
+ * @ndev: The nci spi device
+ * Context: can sleep
+ *
+ * This call may only be used from a context that may sleep.  The sleep
+ * is non-interruptible, and has no timeout.
+ *
+ * It returns zero on success, else a negative error code.
+ */
+int nci_spi_recv_frame(struct nci_spi_dev *ndev)
+{
+       struct sk_buff *skb;
+       int ret = 0;
+
+       ndev->ops->deassert_int(ndev);
+
+       /* Retrieve frame from SPI */
+       skb = __nci_spi_recv_frame(ndev);
+       if (!skb) {
+               ret = -EIO;
+               goto done;
+       }
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) {
+               if (!nci_spi_check_crc(skb)) {
+                       send_acknowledge(ndev, ACKNOWLEDGE_NACK);
+                       goto done;
+               }
+
+               /* In case of acknowledged mode: if ACK or NACK received,
+                * unblock completion of latest frame sent.
+                */
+               ndev->req_result = nci_spi_get_ack(skb);
+               if (ndev->req_result)
+                       complete(&ndev->req_completion);
+       }
+
+       /* If there is no payload (ACK/NACK only frame),
+        * free the socket buffer
+        */
+       if (skb->len == 0) {
+               kfree_skb(skb);
+               goto done;
+       }
+
+       if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED)
+               send_acknowledge(ndev, ACKNOWLEDGE_ACK);
+
+       /* Forward skb to NCI core layer */
+       ret = nci_recv_frame(ndev->nci_dev, skb);
+
+done:
+       ndev->ops->assert_int(ndev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nci_spi_recv_frame);
index f0c4d61..b05ad90 100644 (file)
@@ -56,6 +56,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 },
        [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 },
        [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED },
+       [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING,
+                                    .len = NFC_FIRMWARE_NAME_MAXSIZE },
 };
 
 static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = {
@@ -424,6 +426,69 @@ free_msg:
        return rc;
 }
 
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_SE_ADDED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
+           nla_put_u8(msg, NFC_ATTR_SE_TYPE, type))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_SE_REMOVED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+           nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
 static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
                                u32 portid, u32 seq,
                                struct netlink_callback *cb,
@@ -442,7 +507,6 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
        if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
            nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
            nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
-           nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) ||
            nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
            nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
                goto nla_put_failure;
@@ -1025,6 +1089,108 @@ exit:
        return rc;
 }
 
+static int nfc_genl_fw_upload(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx;
+       char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       nla_strlcpy(firmware_name, info->attrs[NFC_ATTR_FIRMWARE_NAME],
+                   sizeof(firmware_name));
+
+       rc = nfc_fw_upload(dev, firmware_name);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_CMD_FW_UPLOAD);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
+           nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+static int nfc_genl_enable_se(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx, se_idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       rc = nfc_enable_se(dev, se_idx);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
+static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nfc_dev *dev;
+       int rc;
+       u32 idx, se_idx;
+
+       if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+           !info->attrs[NFC_ATTR_SE_INDEX])
+               return -EINVAL;
+
+       idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+       se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]);
+
+       dev = nfc_get_device(idx);
+       if (!dev)
+               return -ENODEV;
+
+       rc = nfc_disable_se(dev, se_idx);
+
+       nfc_put_device(dev);
+       return rc;
+}
+
 static struct genl_ops nfc_genl_ops[] = {
        {
                .cmd = NFC_CMD_GET_DEVICE,
@@ -1084,6 +1250,21 @@ static struct genl_ops nfc_genl_ops[] = {
                .doit = nfc_genl_llc_sdreq,
                .policy = nfc_genl_policy,
        },
+       {
+               .cmd = NFC_CMD_FW_UPLOAD,
+               .doit = nfc_genl_fw_upload,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_ENABLE_SE,
+               .doit = nfc_genl_enable_se,
+               .policy = nfc_genl_policy,
+       },
+       {
+               .cmd = NFC_CMD_DISABLE_SE,
+               .doit = nfc_genl_disable_se,
+               .policy = nfc_genl_policy,
+       },
 };
 
 
index afa1f84..ee85a1f 100644 (file)
@@ -94,6 +94,9 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev);
 
 int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list);
 
+int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type);
+int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx);
+
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
 static inline void nfc_put_device(struct nfc_dev *dev)
@@ -120,6 +123,11 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter)
        class_dev_iter_exit(iter);
 }
 
+int nfc_fw_upload(struct nfc_dev *dev, const char *firmware_name);
+int nfc_genl_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
+int nfc_fw_upload_done(struct nfc_dev *dev, const char *firmware_name);
+
 int nfc_dev_up(struct nfc_dev *dev);
 
 int nfc_dev_down(struct nfc_dev *dev);
@@ -139,4 +147,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx);
 int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                      data_exchange_cb_t cb, void *cb_context);
 
+int nfc_enable_se(struct nfc_dev *dev, u32 se_idx);
+int nfc_disable_se(struct nfc_dev *dev, u32 se_idx);
+
 #endif /* __LOCAL_NFC_H */
index 3a8d190..943e5c4 100644 (file)
@@ -16,7 +16,7 @@
  * 02110-1301, USA
  */
 
-#ifdef CONFIG_NET_IPGRE_DEMUX
+#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/if.h>
index f52dfb9..ba81294 100644 (file)
@@ -39,7 +39,7 @@ static const struct vport_ops *vport_ops_list[] = {
        &ovs_netdev_vport_ops,
        &ovs_internal_vport_ops,
 
-#ifdef CONFIG_NET_IPGRE_DEMUX
+#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
        &ovs_gre_vport_ops,
 #endif
 };
index bf6e6bd..9a383a8 100644 (file)
@@ -102,13 +102,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
        sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
 
        asoc->state = SCTP_STATE_CLOSED;
-
-       /* Set these values from the socket values, a conversion between
-        * millsecons to seconds/microseconds must also be done.
-        */
-       asoc->cookie_life.tv_sec = sp->assocparams.sasoc_cookie_life / 1000;
-       asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000)
-                                       * 1000;
+       asoc->cookie_life = ms_to_ktime(sp->assocparams.sasoc_cookie_life);
        asoc->frag_point = 0;
        asoc->user_frag = sp->user_frag;
 
index a8b2674..b26999d 100644 (file)
@@ -247,10 +247,9 @@ void sctp_endpoint_free(struct sctp_endpoint *ep)
 /* Final destructor for endpoint.  */
 static void sctp_endpoint_destroy(struct sctp_endpoint *ep)
 {
-       SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
+       struct sock *sk;
 
-       /* Free up the HMAC transform. */
-       crypto_free_hash(sctp_sk(ep->base.sk)->hmac);
+       SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
 
        /* Free the digest buffer */
        kfree(ep->digest);
@@ -271,13 +270,15 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep)
 
        memset(ep->secret_key, 0, sizeof(ep->secret_key));
 
-       /* Remove and free the port */
-       if (sctp_sk(ep->base.sk)->bind_hash)
-               sctp_put_port(ep->base.sk);
-
        /* Give up our hold on the sock. */
-       if (ep->base.sk)
-               sock_put(ep->base.sk);
+       sk = ep->base.sk;
+       if (sk != NULL) {
+               /* Remove and free the port */
+               if (sctp_sk(sk)->bind_hash)
+                       sctp_put_port(sk);
+
+               sock_put(sk);
+       }
 
        kfree(ep);
        SCTP_DBG_OBJCNT_DEC(ep);
index 0c83162..62526c4 100644 (file)
@@ -138,7 +138,7 @@ static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_commo
 
                peer = asoc->peer.primary_path;
                if (unlikely(peer == NULL)) {
-                       WARN(1, "Association %p with NULL primary path!", asoc);
+                       WARN(1, "Association %p with NULL primary path!\n", asoc);
                        return;
                }
 
index fc85487..dd71f1f 100644 (file)
@@ -1630,8 +1630,8 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
        cookie->c.adaptation_ind = asoc->peer.adaptation_ind;
 
        /* Set an expiration time for the cookie.  */
-       do_gettimeofday(&cookie->c.expiration);
-       TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration);
+       cookie->c.expiration = ktime_add(asoc->cookie_life,
+                                        ktime_get());
 
        /* Copy the peer's init packet.  */
        memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
@@ -1680,7 +1680,7 @@ struct sctp_association *sctp_unpack_cookie(
        unsigned int len;
        sctp_scope_t scope;
        struct sk_buff *skb = chunk->skb;
-       struct timeval tv;
+       ktime_t kt;
        struct hash_desc desc;
 
        /* Header size is static data prior to the actual cookie, including
@@ -1757,11 +1757,11 @@ no_hmac:
         * down the new association establishment instead of every packet.
         */
        if (sock_flag(ep->base.sk, SOCK_TIMESTAMP))
-               skb_get_timestamp(skb, &tv);
+               kt = skb_get_ktime(skb);
        else
-               do_gettimeofday(&tv);
+               kt = ktime_get();
 
-       if (!asoc && tv_lt(bear_cookie->expiration, tv)) {
+       if (!asoc && ktime_compare(bear_cookie->expiration, kt) < 0) {
                /*
                 * Section 3.3.10.3 Stale Cookie Error (3)
                 *
@@ -1773,9 +1773,7 @@ no_hmac:
                len = ntohs(chunk->chunk_hdr->length);
                *errp = sctp_make_op_error_space(asoc, chunk, len);
                if (*errp) {
-                       suseconds_t usecs = (tv.tv_sec -
-                               bear_cookie->expiration.tv_sec) * 1000000L +
-                               tv.tv_usec - bear_cookie->expiration.tv_usec;
+                       suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration));
                        __be32 n = htonl(usecs);
 
                        sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE,
@@ -2514,8 +2512,7 @@ do_addr_param:
                /* Suggested Cookie Life span increment's unit is msec,
                 * (1/1000sec).
                 */
-               asoc->cookie_life.tv_sec += stale / 1000;
-               asoc->cookie_life.tv_usec += (stale % 1000) * 1000;
+               asoc->cookie_life = ktime_add_ms(asoc->cookie_life, stale);
                break;
 
        case SCTP_PARAM_HOST_NAME_ADDRESS:
index 32db19b..66fcdcf 100644 (file)
@@ -93,6 +93,7 @@ static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
 static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
 static int sctp_wait_for_accept(struct sock *sk, long timeo);
 static void sctp_wait_for_close(struct sock *sk, long timeo);
+static void sctp_destruct_sock(struct sock *sk);
 static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
                                        union sctp_addr *addr, int len);
 static int sctp_bindx_add(struct sock *, struct sockaddr *, int);
@@ -2910,13 +2911,8 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsig
                        asoc->max_retrans = assocparams.sasoc_asocmaxrxt;
                }
 
-               if (assocparams.sasoc_cookie_life != 0) {
-                       asoc->cookie_life.tv_sec =
-                                       assocparams.sasoc_cookie_life / 1000;
-                       asoc->cookie_life.tv_usec =
-                                       (assocparams.sasoc_cookie_life % 1000)
-                                       * 1000;
-               }
+               if (assocparams.sasoc_cookie_life != 0)
+                       asoc->cookie_life = ms_to_ktime(assocparams.sasoc_cookie_life);
        } else {
                /* Set the values to the endpoint */
                struct sctp_sock *sp = sctp_sk(sk);
@@ -3971,6 +3967,8 @@ static int sctp_init_sock(struct sock *sk)
 
        sp->hmac = NULL;
 
+       sk->sk_destruct = sctp_destruct_sock;
+
        SCTP_DBG_OBJCNT_INC(sock);
 
        local_bh_disable();
@@ -4013,6 +4011,17 @@ static void sctp_destroy_sock(struct sock *sk)
        local_bh_enable();
 }
 
+/* Triggered when there are no references on the socket anymore */
+static void sctp_destruct_sock(struct sock *sk)
+{
+       struct sctp_sock *sp = sctp_sk(sk);
+
+       /* Free up the HMAC transform. */
+       crypto_free_hash(sp->hmac);
+
+       inet_sock_destruct(sk);
+}
+
 /* API 4.1.7 shutdown() - TCP Style Syntax
  *     int shutdown(int socket, int how);
  *
@@ -5074,10 +5083,7 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
                assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
                assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
                assocparams.sasoc_local_rwnd = asoc->a_rwnd;
-               assocparams.sasoc_cookie_life = (asoc->cookie_life.tv_sec
-                                               * 1000) +
-                                               (asoc->cookie_life.tv_usec
-                                               / 1000);
+               assocparams.sasoc_cookie_life = ktime_to_ms(asoc->cookie_life);
 
                list_for_each(pos, &asoc->peer.transport_addr_list) {
                        cnt ++;
@@ -6030,7 +6036,6 @@ fail:
  */
 static int sctp_get_port(struct sock *sk, unsigned short snum)
 {
-       long ret;
        union sctp_addr addr;
        struct sctp_af *af = sctp_sk(sk)->pf->af;
 
@@ -6039,9 +6044,7 @@ static int sctp_get_port(struct sock *sk, unsigned short snum)
        addr.v4.sin_port = htons(snum);
 
        /* Note: sk->sk_num gets filled in if ephemeral port request. */
-       ret = sctp_get_port_local(sk, &addr);
-
-       return ret ? 1 : 0;
+       return !!sctp_get_port_local(sk, &addr);
 }
 
 /*
@@ -6856,7 +6859,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
        newsk->sk_reuse = sk->sk_reuse;
 
        newsk->sk_shutdown = sk->sk_shutdown;
-       newsk->sk_destruct = inet_sock_destruct;
+       newsk->sk_destruct = sctp_destruct_sock;
        newsk->sk_family = sk->sk_family;
        newsk->sk_protocol = IPPROTO_SCTP;
        newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
index 3eec3f7..4da14cb 100644 (file)
 #include <net/ll_poll.h>
 
 #ifdef CONFIG_NET_LL_RX_POLL
+unsigned int sysctl_net_ll_read __read_mostly;
 unsigned int sysctl_net_ll_poll __read_mostly;
 #endif
 
@@ -1147,13 +1148,24 @@ EXPORT_SYMBOL(sock_create_lite);
 /* No kernel lock held - perfect */
 static unsigned int sock_poll(struct file *file, poll_table *wait)
 {
+       unsigned int ll_flag = 0;
        struct socket *sock;
 
        /*
         *      We can't return errors to poll, so it's either yes or no.
         */
        sock = file->private_data;
-       return sock->ops->poll(file, sock, wait);
+
+       if (sk_valid_ll(sock->sk)) {
+               /* this socket can poll_ll so tell the system call */
+               ll_flag = POLL_LL;
+
+               /* once, only if requested by syscall */
+               if (wait && (wait->_key & POLL_LL))
+                       sk_poll_ll(sock->sk, 1);
+       }
+
+       return ll_flag | sock->ops->poll(file, sock, wait);
 }
 
 static int sock_mmap(struct file *file, struct vm_area_struct *vma)
index 3f77f42..593071d 100644 (file)
@@ -144,18 +144,18 @@ EXPORT_SYMBOL_GPL(vm_sockets_get_local_cid);
  * VSOCK_HASH_SIZE + 1 so that vsock_bind_table[0] through
  * vsock_bind_table[VSOCK_HASH_SIZE - 1] are for bound sockets and
  * vsock_bind_table[VSOCK_HASH_SIZE] is for unbound sockets.  The hash function
- * mods with VSOCK_HASH_SIZE - 1 to ensure this.
+ * mods with VSOCK_HASH_SIZE to ensure this.
  */
 #define VSOCK_HASH_SIZE         251
 #define MAX_PORT_RETRIES        24
 
-#define VSOCK_HASH(addr)        ((addr)->svm_port % (VSOCK_HASH_SIZE - 1))
+#define VSOCK_HASH(addr)        ((addr)->svm_port % VSOCK_HASH_SIZE)
 #define vsock_bound_sockets(addr) (&vsock_bind_table[VSOCK_HASH(addr)])
 #define vsock_unbound_sockets     (&vsock_bind_table[VSOCK_HASH_SIZE])
 
 /* XXX This can probably be implemented in a better way. */
 #define VSOCK_CONN_HASH(src, dst)                              \
-       (((src)->svm_cid ^ (dst)->svm_port) % (VSOCK_HASH_SIZE - 1))
+       (((src)->svm_cid ^ (dst)->svm_port) % VSOCK_HASH_SIZE)
 #define vsock_connected_sockets(src, dst)              \
        (&vsock_connected_table[VSOCK_CONN_HASH(src, dst)])
 #define vsock_connected_sockets_vsk(vsk)                               \
@@ -165,6 +165,18 @@ static struct list_head vsock_bind_table[VSOCK_HASH_SIZE + 1];
 static struct list_head vsock_connected_table[VSOCK_HASH_SIZE];
 static DEFINE_SPINLOCK(vsock_table_lock);
 
+/* Autobind this socket to the local address if necessary. */
+static int vsock_auto_bind(struct vsock_sock *vsk)
+{
+       struct sock *sk = sk_vsock(vsk);
+       struct sockaddr_vm local_addr;
+
+       if (vsock_addr_bound(&vsk->local_addr))
+               return 0;
+       vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY);
+       return __vsock_bind(sk, &local_addr);
+}
+
 static void vsock_init_tables(void)
 {
        int i;
@@ -956,15 +968,10 @@ static int vsock_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
        lock_sock(sk);
 
-       if (!vsock_addr_bound(&vsk->local_addr)) {
-               struct sockaddr_vm local_addr;
-
-               vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY);
-               err = __vsock_bind(sk, &local_addr);
-               if (err != 0)
-                       goto out;
+       err = vsock_auto_bind(vsk);
+       if (err)
+               goto out;
 
-       }
 
        /* If the provided message contains an address, use that.  Otherwise
         * fall back on the socket's remote handle (if it has been connected).
@@ -1038,15 +1045,9 @@ static int vsock_dgram_connect(struct socket *sock,
 
        lock_sock(sk);
 
-       if (!vsock_addr_bound(&vsk->local_addr)) {
-               struct sockaddr_vm local_addr;
-
-               vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY);
-               err = __vsock_bind(sk, &local_addr);
-               if (err != 0)
-                       goto out;
-
-       }
+       err = vsock_auto_bind(vsk);
+       if (err)
+               goto out;
 
        if (!transport->dgram_allow(remote_addr->svm_cid,
                                    remote_addr->svm_port)) {
@@ -1163,17 +1164,9 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr,
                memcpy(&vsk->remote_addr, remote_addr,
                       sizeof(vsk->remote_addr));
 
-               /* Autobind this socket to the local address if necessary. */
-               if (!vsock_addr_bound(&vsk->local_addr)) {
-                       struct sockaddr_vm local_addr;
-
-                       vsock_addr_init(&local_addr, VMADDR_CID_ANY,
-                                       VMADDR_PORT_ANY);
-                       err = __vsock_bind(sk, &local_addr);
-                       if (err != 0)
-                               goto out;
-
-               }
+               err = vsock_auto_bind(vsk);
+               if (err)
+                       goto out;
 
                sk->sk_state = SS_CONNECTING;
 
index daff752..ffc11df 100644 (file)
@@ -625,13 +625,14 @@ static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg)
 
        /* Attach the packet to the socket's receive queue as an sk_buff. */
        skb = alloc_skb(size, GFP_ATOMIC);
-       if (skb) {
-               /* sk_receive_skb() will do a sock_put(), so hold here. */
-               sock_hold(sk);
-               skb_put(skb, size);
-               memcpy(skb->data, dg, size);
-               sk_receive_skb(sk, skb, 0);
-       }
+       if (!skb)
+               return VMCI_ERROR_NO_MEM;
+
+       /* sk_receive_skb() will do a sock_put(), so hold here. */
+       sock_hold(sk);
+       skb_put(skb, size);
+       memcpy(skb->data, dg, size);
+       sk_receive_skb(sk, skb, 0);
 
        return VMCI_SUCCESS;
 }
@@ -939,10 +940,9 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
                 * reset to prevent that.
                 */
                vmci_transport_send_reset(sk, pkt);
-               goto out;
+               break;
        }
 
-out:
        release_sock(sk);
        kfree(recv_pkt_info);
        /* Release reference obtained in the stream callback when we fetched
index e4df774..f277246 100644 (file)
@@ -301,6 +301,9 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
                return NULL;
        }
 
+       /* atomic_inc_return makes it start at 1, make it start at 0 */
+       rdev->wiphy_idx--;
+
        /* give it a proper name */
        dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
 
@@ -449,8 +452,13 @@ int wiphy_register(struct wiphy *wiphy)
        u16 ifmodes = wiphy->interface_modes;
 
 #ifdef CONFIG_PM
-       if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
-                   !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+       if (WARN_ON(wiphy->wowlan &&
+                   (wiphy->wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+                   !(wiphy->wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+               return -EINVAL;
+       if (WARN_ON(wiphy->wowlan &&
+                   !wiphy->wowlan->flags && !wiphy->wowlan->n_patterns &&
+                   !wiphy->wowlan->tcp))
                return -EINVAL;
 #endif
 
@@ -540,25 +548,28 @@ int wiphy_register(struct wiphy *wiphy)
        }
 
 #ifdef CONFIG_PM
-       if (rdev->wiphy.wowlan.n_patterns) {
-               if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len ||
-                           rdev->wiphy.wowlan.pattern_min_len >
-                           rdev->wiphy.wowlan.pattern_max_len))
-                       return -EINVAL;
-       }
+       if (WARN_ON(rdev->wiphy.wowlan && rdev->wiphy.wowlan->n_patterns &&
+                   (!rdev->wiphy.wowlan->pattern_min_len ||
+                    rdev->wiphy.wowlan->pattern_min_len >
+                               rdev->wiphy.wowlan->pattern_max_len)))
+               return -EINVAL;
 #endif
 
        /* check and set up bitrates */
        ieee80211_set_bitrate_flags(wiphy);
 
-       rtnl_lock();
 
        res = device_add(&rdev->wiphy.dev);
+       if (res)
+               return res;
+
+       res = rfkill_register(rdev->rfkill);
        if (res) {
-               rtnl_unlock();
+               device_del(&rdev->wiphy.dev);
                return res;
        }
 
+       rtnl_lock();
        /* set up regulatory info */
        wiphy_regulatory_register(wiphy);
 
@@ -585,17 +596,6 @@ int wiphy_register(struct wiphy *wiphy)
 
        cfg80211_debugfs_rdev_add(rdev);
 
-       res = rfkill_register(rdev->rfkill);
-       if (res) {
-               device_del(&rdev->wiphy.dev);
-
-               debugfs_remove_recursive(rdev->wiphy.debugfsdir);
-               list_del_rcu(&rdev->list);
-               wiphy_regulatory_deregister(wiphy);
-               rtnl_unlock();
-               return res;
-       }
-
        rdev->wiphy.registered = true;
        rtnl_unlock();
        return 0;
@@ -632,11 +632,11 @@ void wiphy_unregister(struct wiphy *wiphy)
                rtnl_unlock();
                __count == 0; }));
 
+       rfkill_unregister(rdev->rfkill);
+
        rtnl_lock();
        rdev->wiphy.registered = false;
 
-       rfkill_unregister(rdev->rfkill);
-
        BUG_ON(!list_empty(&rdev->wdev_list));
 
        /*
@@ -816,7 +816,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        pr_err("failed to add phy80211 symlink to netdev!\n");
                }
                wdev->netdev = dev;
-               wdev->sme_state = CFG80211_SME_IDLE;
 #ifdef CONFIG_CFG80211_WEXT
                wdev->wext.default_key = -1;
                wdev->wext.default_mgmt_key = -1;
index a65eaf8..a6b45bf 100644 (file)
@@ -308,11 +308,6 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                           bool local_state_change);
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
-void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
-                              const u8 *req_ie, size_t req_ie_len,
-                              const u8 *resp_ie, size_t resp_ie_len,
-                              u16 status, bool wextev,
-                              struct cfg80211_bss *bss);
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
                                u16 frame_type, const u8 *match_data,
                                int match_len);
@@ -328,12 +323,19 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
 void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
                                const struct ieee80211_vht_cap *vht_capa_mask);
 
-/* SME */
+/* SME events */
 int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     struct net_device *dev,
                     struct cfg80211_connect_params *connect,
                     struct cfg80211_cached_keys *connkeys,
                     const u8 *prev_bssid);
+void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+                              const u8 *req_ie, size_t req_ie_len,
+                              const u8 *resp_ie, size_t resp_ie_len,
+                              u16 status, bool wextev,
+                              struct cfg80211_bss *bss);
+void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
+                            size_t ie_len, u16 reason, bool from_ap);
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, u16 reason,
                        bool wextev);
@@ -344,21 +346,21 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
 int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev);
 
+/* SME implementation */
 void cfg80211_conn_work(struct work_struct *work);
-void cfg80211_sme_failed_assoc(struct wireless_dev *wdev);
-bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev);
+void cfg80211_sme_scan_done(struct net_device *dev);
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
+void cfg80211_sme_disassoc(struct wireless_dev *wdev);
+void cfg80211_sme_deauth(struct wireless_dev *wdev);
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
 
 /* internal helpers */
 bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
 int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                                   struct key_params *params, int key_idx,
                                   bool pairwise, const u8 *mac_addr);
-void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
-                            size_t ie_len, u16 reason, bool from_ap);
-void cfg80211_sme_scan_done(struct net_device *dev);
-void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
-void cfg80211_sme_disassoc(struct net_device *dev,
-                          struct cfg80211_internal_bss *bss);
 void __cfg80211_scan_done(struct work_struct *wk);
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak);
 void __cfg80211_sched_scan_results(struct work_struct *wk);
index 5449c5a..39bff7d 100644 (file)
@@ -43,7 +43,6 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
@@ -64,8 +63,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
 
        trace_cfg80211_ibss_joined(dev, bssid);
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
-
        ev = kzalloc(sizeof(*ev), gfp);
        if (!ev)
                return;
@@ -120,7 +117,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
-       wdev->sme_state = CFG80211_SME_CONNECTING;
 
        err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
                                    params->channel_fixed
@@ -134,7 +130,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        err = rdev_join_ibss(rdev, dev, params);
        if (err) {
                wdev->connect_keys = NULL;
-               wdev->sme_state = CFG80211_SME_IDLE;
                return err;
        }
 
@@ -186,7 +181,6 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
        }
 
        wdev->current_bss = NULL;
-       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 #ifdef CONFIG_CFG80211_WEXT
        if (!nowext)
index 5dfb289..30c4920 100644 (file)
@@ -18,6 +18,7 @@
 #define MESH_PATH_TO_ROOT_TIMEOUT      6000
 #define MESH_ROOT_INTERVAL     5000
 #define MESH_ROOT_CONFIRMATION_INTERVAL 2000
+#define MESH_DEFAULT_PLINK_TIMEOUT     1800 /* timeout in seconds */
 
 /*
  * Minimum interval between two consecutive PREQs originated by the same
@@ -75,6 +76,7 @@ const struct mesh_config default_mesh_config = {
        .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
        .power_mode = NL80211_MESH_POWER_ACTIVE,
        .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
+       .plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT,
 };
 
 const struct mesh_setup default_mesh_setup = {
@@ -160,6 +162,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
        }
 
+       /*
+        * check if basic rates are available otherwise use mandatory rates as
+        * basic rates
+        */
+       if (!setup->basic_rates) {
+               struct ieee80211_supported_band *sband =
+                               rdev->wiphy.bands[setup->chandef.chan->band];
+               setup->basic_rates = ieee80211_mandatory_rates(sband);
+       }
+
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
                return -EINVAL;
 
index 7bde5d9..a61a44b 100644 (file)
 #include "rdev-ops.h"
 
 
-void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       trace_cfg80211_send_rx_auth(dev);
-
-       nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
-       cfg80211_sme_rx_auth(dev, buf, len);
-}
-EXPORT_SYMBOL(cfg80211_send_rx_auth);
-
-void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
+void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
                            const u8 *buf, size_t len)
 {
-       u16 status_code;
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        u8 *ie = mgmt->u.assoc_resp.variable;
        int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+       u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 
        trace_cfg80211_send_rx_assoc(dev, bss);
 
-       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
-
        /*
         * This is a bit of a hack, we don't notify userspace of
         * a (re-)association reply if we tried to send a reassoc
         * and got a reject -- we only try again with an assoc
         * frame instead of reassoc.
         */
-       if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
-           cfg80211_sme_failed_reassoc(wdev)) {
+       if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
                cfg80211_put_bss(wiphy, bss);
                return;
        }
 
        nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
-
-       if (status_code != WLAN_STATUS_SUCCESS && wdev->conn) {
-               cfg80211_sme_failed_assoc(wdev);
-               /*
-                * do not call connect_result() now because the
-                * sme will schedule work that does it later.
-                */
-               cfg80211_put_bss(wiphy, bss);
-               return;
-       }
-
-       if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
-               /*
-                * This is for the userspace SME, the CONNECTING
-                * state will be changed to CONNECTED by
-                * __cfg80211_connect_result() below.
-                */
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-       }
-
-       /* this consumes the bss reference */
+       /* update current_bss etc., consumes the bss reference */
        __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
                                  status_code,
                                  status_code == WLAN_STATUS_SUCCESS, bss);
 }
-EXPORT_SYMBOL(cfg80211_send_rx_assoc);
+EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
 
-void cfg80211_send_deauth(struct net_device *dev,
-                         const u8 *buf, size_t len)
+static void cfg80211_process_auth(struct wireless_dev *wdev,
+                                 const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
+       cfg80211_sme_rx_auth(wdev, buf, len);
+}
+
+static void cfg80211_process_deauth(struct wireless_dev *wdev,
+                                   const u8 *buf, size_t len)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        const u8 *bssid = mgmt->bssid;
-       bool was_current = false;
+       u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+       bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
 
-       trace_cfg80211_send_deauth(dev);
-       ASSERT_WDEV_LOCK(wdev);
+       nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
 
-       if (wdev->current_bss &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-               was_current = true;
-       }
+       if (!wdev->current_bss ||
+           !ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
+               return;
+
+       __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+       cfg80211_sme_deauth(wdev);
+}
 
-       nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
+static void cfg80211_process_disassoc(struct wireless_dev *wdev,
+                                     const u8 *buf, size_t len)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
+       const u8 *bssid = mgmt->bssid;
+       u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+       bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
 
-       if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) {
-               u16 reason_code;
-               bool from_ap;
+       nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
 
-               reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+       if (WARN_ON(!wdev->current_bss ||
+                   !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+               return;
 
-               from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
-               __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
-       } else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
-               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
-       }
+       __cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
+       cfg80211_sme_disassoc(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_deauth);
 
-void cfg80211_send_disassoc(struct net_device *dev,
-                           const u8 *buf, size_t len)
+void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
-       const u8 *bssid = mgmt->bssid;
-       u16 reason_code;
-       bool from_ap;
+       struct ieee80211_mgmt *mgmt = (void *)buf;
 
-       trace_cfg80211_send_disassoc(dev);
        ASSERT_WDEV_LOCK(wdev);
 
-       nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
+       trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
+       if (WARN_ON(len < 2))
                return;
 
-       if (wdev->current_bss &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
-               cfg80211_sme_disassoc(dev, wdev->current_bss);
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-       } else
-               WARN_ON(1);
-
-
-       reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
-
-       from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
-       __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
+       if (ieee80211_is_auth(mgmt->frame_control))
+               cfg80211_process_auth(wdev, buf, len);
+       else if (ieee80211_is_deauth(mgmt->frame_control))
+               cfg80211_process_deauth(wdev, buf, len);
+       else if (ieee80211_is_disassoc(mgmt->frame_control))
+               cfg80211_process_disassoc(wdev, buf, len);
 }
-EXPORT_SYMBOL(cfg80211_send_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
 
-void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
+void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
@@ -170,14 +127,11 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
        trace_cfg80211_send_auth_timeout(dev, addr);
 
        nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
-       if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
+       cfg80211_sme_auth_timeout(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_auth_timeout);
+EXPORT_SYMBOL(cfg80211_auth_timeout);
 
-void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
+void cfg80211_assoc_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
@@ -186,12 +140,28 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
        trace_cfg80211_send_assoc_timeout(dev, addr);
 
        nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
-       if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         false, NULL);
+       cfg80211_sme_assoc_timeout(wdev);
 }
-EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
+EXPORT_SYMBOL(cfg80211_assoc_timeout);
+
+void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct ieee80211_mgmt *mgmt = (void *)buf;
+
+       ASSERT_WDEV_LOCK(wdev);
+
+       trace_cfg80211_tx_mlme_mgmt(dev, buf, len);
+
+       if (WARN_ON(len < 2))
+               return;
+
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cfg80211_process_deauth(wdev, buf, len);
+       else
+               cfg80211_process_disassoc(wdev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
 
 void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
                                  enum nl80211_key_type key_type, int key_id,
@@ -314,21 +284,12 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
-       bool was_connected = false;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->current_bss && req->prev_bssid &&
-           ether_addr_equal(wdev->current_bss->pub.bssid, req->prev_bssid)) {
-               /*
-                * Trying to reassociate: Allow this to proceed and let the old
-                * association to be dropped when the new one is completed.
-                */
-               if (wdev->sme_state == CFG80211_SME_CONNECTED) {
-                       was_connected = true;
-                       wdev->sme_state = CFG80211_SME_CONNECTING;
-               }
-       } else if (wdev->current_bss)
+       if (wdev->current_bss &&
+           (!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
+                                                  req->prev_bssid)))
                return -EALREADY;
 
        cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
@@ -338,11 +299,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
 
        req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
                                    WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
-       if (!req->bss) {
-               if (was_connected)
-                       wdev->sme_state = CFG80211_SME_CONNECTED;
+       if (!req->bss)
                return -ENOENT;
-       }
 
        err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
        if (err)
@@ -351,11 +309,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
        err = rdev_assoc(rdev, dev, req);
 
 out:
-       if (err) {
-               if (was_connected)
-                       wdev->sme_state = CFG80211_SME_CONNECTED;
+       if (err)
                cfg80211_put_bss(&rdev->wiphy, req->bss);
-       }
 
        return err;
 }
@@ -376,8 +331,9 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (local_state_change && (!wdev->current_bss ||
-           !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
+       if (local_state_change &&
+           (!wdev->current_bss ||
+            !ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
                return 0;
 
        return rdev_deauth(rdev, dev, &req);
@@ -395,13 +351,11 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
                .ie = ie,
                .ie_len = ie_len,
        };
+       int err;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               return -ENOTCONN;
-
-       if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
+       if (!wdev->current_bss)
                return -ENOTCONN;
 
        if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
@@ -409,7 +363,13 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
        else
                return -ENOTCONN;
 
-       return rdev_disassoc(rdev, dev, &req);
+       err = rdev_disassoc(rdev, dev, &req);
+       if (err)
+               return err;
+
+       /* driver should have reported the disassoc */
+       WARN_ON(wdev->current_bss);
+       return 0;
 }
 
 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
@@ -417,10 +377,6 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        u8 bssid[ETH_ALEN];
-       struct cfg80211_deauth_request req = {
-               .reason_code = WLAN_REASON_DEAUTH_LEAVING,
-               .bssid = bssid,
-       };
 
        ASSERT_WDEV_LOCK(wdev);
 
@@ -431,13 +387,8 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                return;
 
        memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
-       rdev_deauth(rdev, dev, &req);
-
-       if (wdev->current_bss) {
-               cfg80211_unhold_bss(wdev->current_bss);
-               cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
-               wdev->current_bss = NULL;
-       }
+       cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
+                            WLAN_REASON_DEAUTH_LEAVING, false);
 }
 
 struct cfg80211_mgmt_registration {
index ea74b9d..e545023 100644 (file)
@@ -800,12 +800,9 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        case NL80211_IFTYPE_MESH_POINT:
                break;
        case NL80211_IFTYPE_ADHOC:
-               if (!wdev->current_bss)
-                       return -ENOLINK;
-               break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (wdev->sme_state != CFG80211_SME_CONNECTED)
+               if (!wdev->current_bss)
                        return -ENOLINK;
                break;
        default:
@@ -908,7 +905,7 @@ nla_put_failure:
 static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
                                        struct sk_buff *msg)
 {
-       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
        struct nlattr *nl_tcp;
 
        if (!tcp)
@@ -951,37 +948,37 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 {
        struct nlattr *nl_wowlan;
 
-       if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+       if (!dev->wiphy.wowlan)
                return 0;
 
        nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
        if (!nl_wowlan)
                return -ENOBUFS;
 
-       if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
+       if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                return -ENOBUFS;
 
-       if (dev->wiphy.wowlan.n_patterns) {
+       if (dev->wiphy.wowlan->n_patterns) {
                struct nl80211_wowlan_pattern_support pat = {
-                       .max_patterns = dev->wiphy.wowlan.n_patterns,
-                       .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
-                       .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
-                       .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
+                       .max_patterns = dev->wiphy.wowlan->n_patterns,
+                       .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
+                       .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
+                       .max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset,
                };
 
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@ -3986,10 +3983,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        params.listen_interval =
                nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
 
-       if (info->attrs[NL80211_ATTR_STA_AID])
-               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
-       else
+       if (info->attrs[NL80211_ATTR_PEER_AID])
                params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+       else
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
        if (!params.aid || params.aid > IEEE80211_MAX_AID)
                return -EINVAL;
 
@@ -4041,7 +4038,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                /* but don't bother the driver with it */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
@@ -4067,7 +4065,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
                        return -EINVAL;
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
@@ -4589,7 +4588,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
            nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
                        cur_params.power_mode) ||
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
-                       cur_params.dot11MeshAwakeWindowDuration))
+                       cur_params.dot11MeshAwakeWindowDuration) ||
+           nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+                       cur_params.plink_timeout))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@ -4630,6 +4631,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
 };
 
 static const struct nla_policy
@@ -4767,6 +4769,9 @@ do {                                                                          \
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
+                                 mask, NL80211_MESHCONF_PLINK_TIMEOUT,
+                                 nla_get_u32);
        if (mask_out)
                *mask_out = mask;
 
@@ -7153,6 +7158,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
 
        switch (wdev->iftype) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+                       return -EINVAL;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_CLIENT:
@@ -7160,7 +7168,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_P2P_DEVICE:
                break;
        default:
                return -EOPNOTSUPP;
@@ -7188,9 +7195,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
-       err = nl80211_parse_chandef(rdev, info, &chandef);
-       if (err)
-               return err;
+       /* get the channel if any has been specified, otherwise pass NULL to
+        * the driver. The latter will use the current one
+        */
+       chandef.chan = NULL;
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               err = nl80211_parse_chandef(rdev, info, &chandef);
+               if (err)
+                       return err;
+       }
+
+       if (!chandef.chan && offchan)
+               return -EINVAL;
 
        if (!dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -7495,6 +7511,23 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                setup.chandef.chan = NULL;
        }
 
+       if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+               u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               int n_rates =
+                       nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               struct ieee80211_supported_band *sband;
+
+               if (!setup.chandef.chan)
+                       return -EINVAL;
+
+               sband = rdev->wiphy.bands[setup.chandef.chan->band];
+
+               err = ieee80211_get_ratemask(sband, rates, n_rates,
+                                            &setup.basic_rates);
+               if (err)
+                       return err;
+       }
+
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
 }
 
@@ -7591,8 +7624,7 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
        void *hdr;
        u32 size = NLMSG_DEFAULT_SIZE;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan)
                return -EOPNOTSUPP;
 
        if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
@@ -7665,7 +7697,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
        int err, port;
 
-       if (!rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan->tcp)
                return -EINVAL;
 
        err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
@@ -7685,16 +7717,16 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
-       if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
+       if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+                       rdev->wiphy.wowlan->tcp->data_interval_max ||
            nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
-       if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
+       if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
                return -EINVAL;
 
        wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
@@ -7709,13 +7741,13 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
                if (!tok->len || tokens_size % tok->len)
                        return -EINVAL;
-               if (!rdev->wiphy.wowlan.tcp->tok)
+               if (!rdev->wiphy.wowlan->tcp->tok)
                        return -EINVAL;
-               if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
+               if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
                        return -EINVAL;
-               if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
+               if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
                        return -EINVAL;
-               if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
+               if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
                        return -EINVAL;
                if (tok->offset + tok->len > data_size)
                        return -EINVAL;
@@ -7723,7 +7755,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
        if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
                seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
-               if (!rdev->wiphy.wowlan.tcp->seq)
+               if (!rdev->wiphy.wowlan->tcp->seq)
                        return -EINVAL;
                if (seq->len == 0 || seq->len > 4)
                        return -EINVAL;
@@ -7804,12 +7836,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
        struct cfg80211_wowlan new_triggers = {};
        struct cfg80211_wowlan *ntrig;
-       struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
+       const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
        bool prev_enabled = rdev->wiphy.wowlan_config;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!wowlan)
                return -EOPNOTSUPP;
 
        if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
@@ -9326,31 +9357,27 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len)
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
+                                 size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       const struct ieee80211_mgmt *mgmt = (void *)buf;
+       u32 cmd;
 
-       trace_cfg80211_send_unprot_deauth(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
+       if (WARN_ON(len < 2))
+               return;
 
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
+       else
+               cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
 
-       trace_cfg80211_send_unprot_disassoc(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
+       trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
+       nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC);
 }
-EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9861,7 +9888,6 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
 
        if (!nlportid)
@@ -9882,12 +9908,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return true;
-       }
-
+       genlmsg_end(msg, hdr);
        genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
        return true;
 
@@ -10330,10 +10351,7 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10399,7 +10417,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_probe_status(dev, addr, cookie, acked);
 
@@ -10421,11 +10438,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
            (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10491,7 +10504,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err, size = 200;
+       int size = 200;
 
        trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
 
@@ -10577,9 +10590,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                nla_nest_end(msg, reasons);
        }
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0)
-               goto free_msg;
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10599,7 +10610,6 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
                                         reason_code);
@@ -10622,11 +10632,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
             nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10684,7 +10690,6 @@ void cfg80211_ft_event(struct net_device *netdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_ft_event(wiphy, netdev, ft_event);
 
@@ -10710,11 +10715,7 @@ void cfg80211_ft_event(struct net_device *netdev,
                nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
                        ft_event->ric_ies);
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, GFP_KERNEL);
index e1d6749..5a24c98 100644 (file)
@@ -1345,7 +1345,7 @@ get_reg_request_treatment(struct wiphy *wiphy,
                                return REG_REQ_OK;
                        return REG_REQ_ALREADY_SET;
                }
-               return 0;
+               return REG_REQ_OK;
        case NL80211_REGDOM_SET_BY_DRIVER:
                if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
                        if (regdom_changes(pending_request->alpha2))
index 81be95f..ae7e2cb 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * SME code for cfg80211's connect emulation.
+ * SME code for cfg80211
+ * both driver SME event handling and the SME implementation
+ * (for nl80211's connect() and wext)
  *
  * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
  * Copyright (C) 2009   Intel Corporation. All rights reserved.
 #include "reg.h"
 #include "rdev-ops.h"
 
+/*
+ * Software SME in cfg80211, using auth/assoc/deauth calls to the
+ * driver. This is is for implementing nl80211's connect/disconnect
+ * and wireless extensions (if configured.)
+ */
+
 struct cfg80211_conn {
        struct cfg80211_connect_params params;
        /* these are sub-states of the _CONNECTING sme_state */
        enum {
-               CFG80211_CONN_IDLE,
                CFG80211_CONN_SCANNING,
                CFG80211_CONN_SCAN_AGAIN,
                CFG80211_CONN_AUTHENTICATE_NEXT,
                CFG80211_CONN_AUTHENTICATING,
                CFG80211_CONN_ASSOCIATE_NEXT,
                CFG80211_CONN_ASSOCIATING,
-               CFG80211_CONN_DEAUTH_ASSOC_FAIL,
+               CFG80211_CONN_DEAUTH,
+               CFG80211_CONN_CONNECTED,
        } state;
        u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
        u8 *ie;
@@ -37,39 +45,16 @@ struct cfg80211_conn {
        bool auto_auth, prev_bssid_valid;
 };
 
-static bool cfg80211_is_all_idle(void)
+static void cfg80211_sme_free(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev;
-       struct wireless_dev *wdev;
-       bool is_all_idle = true;
-
-       /*
-        * All devices must be idle as otherwise if you are actively
-        * scanning some new beacon hints could be learned and would
-        * count as new regulatory hints.
-        */
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
-               list_for_each_entry(wdev, &rdev->wdev_list, list) {
-                       wdev_lock(wdev);
-                       if (wdev->sme_state != CFG80211_SME_IDLE)
-                               is_all_idle = false;
-                       wdev_unlock(wdev);
-               }
-       }
-
-       return is_all_idle;
-}
+       if (!wdev->conn)
+               return;
 
-static void disconnect_work(struct work_struct *work)
-{
-       rtnl_lock();
-       if (cfg80211_is_all_idle())
-               regulatory_hint_disconnect();
-       rtnl_unlock();
+       kfree(wdev->conn->ie);
+       kfree(wdev->conn);
+       wdev->conn = NULL;
 }
 
-static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
-
 static int cfg80211_conn_scan(struct wireless_dev *wdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
@@ -164,6 +149,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        params = &wdev->conn->params;
 
        switch (wdev->conn->state) {
+       case CFG80211_CONN_SCANNING:
+               /* didn't find it during scan ... */
+               return -ENOENT;
        case CFG80211_CONN_SCAN_AGAIN:
                return cfg80211_conn_scan(wdev);
        case CFG80211_CONN_AUTHENTICATE_NEXT:
@@ -200,12 +188,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                             WLAN_REASON_DEAUTH_LEAVING,
                                             false);
                return err;
-       case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
+       case CFG80211_CONN_DEAUTH:
                cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                     NULL, 0,
                                     WLAN_REASON_DEAUTH_LEAVING, false);
-               /* return an error so that we call __cfg80211_connect_result() */
-               return -EINVAL;
+               return 0;
        default:
                return 0;
        }
@@ -229,7 +216,8 @@ void cfg80211_conn_work(struct work_struct *work)
                        wdev_unlock(wdev);
                        continue;
                }
-               if (wdev->sme_state != CFG80211_SME_CONNECTING || !wdev->conn) {
+               if (!wdev->conn ||
+                   wdev->conn->state == CFG80211_CONN_CONNECTED) {
                        wdev_unlock(wdev);
                        continue;
                }
@@ -237,12 +225,14 @@ void cfg80211_conn_work(struct work_struct *work)
                        memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
                        bssid = bssid_buf;
                }
-               if (cfg80211_conn_do_work(wdev))
+               if (cfg80211_conn_do_work(wdev)) {
                        __cfg80211_connect_result(
                                        wdev->netdev, bssid,
                                        NULL, 0, NULL, 0,
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
                                        false, NULL);
+                       cfg80211_sme_free(wdev);
+               }
                wdev_unlock(wdev);
        }
 
@@ -286,9 +276,6 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
        if (!wdev->conn)
                return;
 
@@ -297,20 +284,10 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
                return;
 
        bss = cfg80211_get_conn_bss(wdev);
-       if (bss) {
+       if (bss)
                cfg80211_put_bss(&rdev->wiphy, bss);
-       } else {
-               /* not found */
-               if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
-                       schedule_work(&rdev->conn_work);
-               else
-                       __cfg80211_connect_result(
-                                       wdev->netdev,
-                                       wdev->conn->params.bssid,
-                                       NULL, 0, NULL, 0,
-                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                       false, NULL);
-       }
+       else
+               schedule_work(&rdev->conn_work);
 }
 
 void cfg80211_sme_scan_done(struct net_device *dev)
@@ -322,10 +299,8 @@ void cfg80211_sme_scan_done(struct net_device *dev)
        wdev_unlock(wdev);
 }
 
-void cfg80211_sme_rx_auth(struct net_device *dev,
-                         const u8 *buf, size_t len)
+void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 {
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
@@ -333,11 +308,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       /* should only RX auth frames when connecting */
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
                return;
 
        if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
@@ -366,46 +337,226 @@ void cfg80211_sme_rx_auth(struct net_device *dev,
                wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
                schedule_work(&rdev->conn_work);
        } else if (status_code != WLAN_STATUS_SUCCESS) {
-               __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
+               __cfg80211_connect_result(wdev->netdev, mgmt->bssid,
+                                         NULL, 0, NULL, 0,
                                          status_code, false, NULL);
-       } else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
-                wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
+       } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
                wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
                schedule_work(&rdev->conn_work);
        }
 }
 
-bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev)
+bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
 {
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
 
-       if (WARN_ON(!wdev->conn))
+       if (!wdev->conn)
                return false;
 
-       if (!wdev->conn->prev_bssid_valid)
+       if (status == WLAN_STATUS_SUCCESS) {
+               wdev->conn->state = CFG80211_CONN_CONNECTED;
                return false;
+       }
 
-       /*
-        * Some stupid APs don't accept reassoc, so we
-        * need to fall back to trying regular assoc.
-        */
-       wdev->conn->prev_bssid_valid = false;
-       wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+       if (wdev->conn->prev_bssid_valid) {
+               /*
+                * Some stupid APs don't accept reassoc, so we
+                * need to fall back to trying regular assoc;
+                * return true so no event is sent to userspace.
+                */
+               wdev->conn->prev_bssid_valid = false;
+               wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
+               schedule_work(&rdev->conn_work);
+               return true;
+       }
+
+       wdev->conn->state = CFG80211_CONN_DEAUTH;
        schedule_work(&rdev->conn_work);
+       return false;
+}
 
-       return true;
+void cfg80211_sme_deauth(struct wireless_dev *wdev)
+{
+       cfg80211_sme_free(wdev);
 }
 
-void cfg80211_sme_failed_assoc(struct wireless_dev *wdev)
+void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 {
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       cfg80211_sme_free(wdev);
+}
 
-       wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL;
+void cfg80211_sme_disassoc(struct wireless_dev *wdev)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+
+       if (!wdev->conn)
+               return;
+
+       wdev->conn->state = CFG80211_CONN_DEAUTH;
        schedule_work(&rdev->conn_work);
 }
 
+void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
+{
+       cfg80211_sme_disassoc(wdev);
+}
+
+static int cfg80211_sme_connect(struct wireless_dev *wdev,
+                               struct cfg80211_connect_params *connect,
+                               const u8 *prev_bssid)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_bss *bss;
+       int err;
+
+       if (!rdev->ops->auth || !rdev->ops->assoc)
+               return -EOPNOTSUPP;
+
+       if (wdev->current_bss)
+               return -EALREADY;
+
+       if (WARN_ON(wdev->conn))
+               return -EINPROGRESS;
+
+       wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
+       if (!wdev->conn)
+               return -ENOMEM;
+
+       /*
+        * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
+        */
+       memcpy(&wdev->conn->params, connect, sizeof(*connect));
+       if (connect->bssid) {
+               wdev->conn->params.bssid = wdev->conn->bssid;
+               memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
+       }
+
+       if (connect->ie) {
+               wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
+                                       GFP_KERNEL);
+               wdev->conn->params.ie = wdev->conn->ie;
+               if (!wdev->conn->ie) {
+                       kfree(wdev->conn);
+                       wdev->conn = NULL;
+                       return -ENOMEM;
+               }
+       }
+
+       if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+               wdev->conn->auto_auth = true;
+               /* start with open system ... should mostly work */
+               wdev->conn->params.auth_type =
+                       NL80211_AUTHTYPE_OPEN_SYSTEM;
+       } else {
+               wdev->conn->auto_auth = false;
+       }
+
+       wdev->conn->params.ssid = wdev->ssid;
+       wdev->conn->params.ssid_len = connect->ssid_len;
+
+       /* see if we have the bss already */
+       bss = cfg80211_get_conn_bss(wdev);
+
+       if (prev_bssid) {
+               memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
+               wdev->conn->prev_bssid_valid = true;
+       }
+
+       /* we're good if we have a matching bss struct */
+       if (bss) {
+               wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
+               err = cfg80211_conn_do_work(wdev);
+               cfg80211_put_bss(wdev->wiphy, bss);
+       } else {
+               /* otherwise we'll need to scan for the AP first */
+               err = cfg80211_conn_scan(wdev);
+
+               /*
+                * If we can't scan right now, then we need to scan again
+                * after the current scan finished, since the parameters
+                * changed (unless we find a good AP anyway).
+                */
+               if (err == -EBUSY) {
+                       err = 0;
+                       wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
+               }
+       }
+
+       if (err)
+               cfg80211_sme_free(wdev);
+
+       return err;
+}
+
+static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       int err;
+
+       if (!wdev->conn)
+               return 0;
+
+       if (!rdev->ops->deauth)
+               return -EOPNOTSUPP;
+
+       if (wdev->conn->state == CFG80211_CONN_SCANNING ||
+           wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
+               err = 0;
+               goto out;
+       }
+
+       /* wdev->conn->params.bssid must be set if > SCANNING */
+       err = cfg80211_mlme_deauth(rdev, wdev->netdev,
+                                  wdev->conn->params.bssid,
+                                  NULL, 0, reason, false);
+ out:
+       cfg80211_sme_free(wdev);
+       return err;
+}
+
+/*
+ * code shared for in-device and software SME
+ */
+
+static bool cfg80211_is_all_idle(void)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       bool is_all_idle = true;
+
+       /*
+        * All devices must be idle as otherwise if you are actively
+        * scanning some new beacon hints could be learned and would
+        * count as new regulatory hints.
+        */
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               list_for_each_entry(wdev, &rdev->wdev_list, list) {
+                       wdev_lock(wdev);
+                       if (wdev->conn || wdev->current_bss)
+                               is_all_idle = false;
+                       wdev_unlock(wdev);
+               }
+       }
+
+       return is_all_idle;
+}
+
+static void disconnect_work(struct work_struct *work)
+{
+       rtnl_lock();
+       if (cfg80211_is_all_idle())
+               regulatory_hint_disconnect();
+       rtnl_unlock();
+}
+
+static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
+
+
+/*
+ * API calls for drivers implementing connect/disconnect and
+ * SME event handling
+ */
+
 void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                               const u8 *req_ie, size_t req_ie_len,
                               const u8 *resp_ie, size_t resp_ie_len,
@@ -424,9 +575,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTING)
-               return;
-
        nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
                                    bssid, req_ie, req_ie_len,
                                    resp_ie, resp_ie_len,
@@ -463,15 +611,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                wdev->current_bss = NULL;
        }
 
-       if (wdev->conn)
-               wdev->conn->state = CFG80211_CONN_IDLE;
-
        if (status != WLAN_STATUS_SUCCESS) {
-               wdev->sme_state = CFG80211_SME_IDLE;
-               if (wdev->conn)
-                       kfree(wdev->conn->ie);
-               kfree(wdev->conn);
-               wdev->conn = NULL;
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
                wdev->ssid_len = 0;
@@ -480,21 +620,16 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        }
 
        if (!bss)
-               bss = cfg80211_get_bss(wdev->wiphy,
-                                      wdev->conn ? wdev->conn->params.channel :
-                                      NULL,
-                                      bssid,
+               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));
        wdev->current_bss = bss_from_pub(bss);
 
-       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        rcu_read_lock();
@@ -530,8 +665,6 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
-
        ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
        if (!ev)
                return;
@@ -572,13 +705,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                goto out;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               goto out;
-
-       /* internal error -- how did we get to CONNECTED w/o BSS? */
-       if (WARN_ON(!wdev->current_bss)) {
+       if (WARN_ON(!wdev->current_bss))
                goto out;
-       }
 
        cfg80211_unhold_bss(wdev->current_bss);
        cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
@@ -628,8 +756,6 @@ void cfg80211_roamed(struct net_device *dev,
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_bss *bss;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
                               wdev->ssid_len, WLAN_CAPABILITY_ESS,
                               WLAN_CAPABILITY_ESS);
@@ -651,8 +777,6 @@ void cfg80211_roamed_bss(struct net_device *dev,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        if (WARN_ON(!bss))
                return;
 
@@ -694,25 +818,14 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
                return;
 
-       if (wdev->sme_state != CFG80211_SME_CONNECTED)
-               return;
-
        if (wdev->current_bss) {
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
        }
 
        wdev->current_bss = NULL;
-       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 
-       if (wdev->conn) {
-               kfree(wdev->conn->ie);
-               wdev->conn->ie = NULL;
-               kfree(wdev->conn);
-               wdev->conn = NULL;
-       }
-
        nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
 
        /*
@@ -741,8 +854,6 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
-
        ev = kzalloc(sizeof(*ev) + ie_len, gfp);
        if (!ev)
                return;
@@ -760,6 +871,9 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
 }
 EXPORT_SYMBOL(cfg80211_disconnected);
 
+/*
+ * API calls for nl80211/wext compatibility code
+ */
 int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     struct net_device *dev,
                     struct cfg80211_connect_params *connect,
@@ -767,14 +881,10 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
                     const u8 *prev_bssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_bss *bss = NULL;
        int err;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE)
-               return -EALREADY;
-
        if (WARN_ON(wdev->connect_keys)) {
                kfree(wdev->connect_keys);
                wdev->connect_keys = NULL;
@@ -810,105 +920,22 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
                }
        }
 
-       if (!rdev->ops->connect) {
-               if (!rdev->ops->auth || !rdev->ops->assoc)
-                       return -EOPNOTSUPP;
-
-               if (WARN_ON(wdev->conn))
-                       return -EINPROGRESS;
-
-               wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
-               if (!wdev->conn)
-                       return -ENOMEM;
-
-               /*
-                * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
-                */
-               memcpy(&wdev->conn->params, connect, sizeof(*connect));
-               if (connect->bssid) {
-                       wdev->conn->params.bssid = wdev->conn->bssid;
-                       memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
-               }
+       wdev->connect_keys = connkeys;
+       memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+       wdev->ssid_len = connect->ssid_len;
 
-               if (connect->ie) {
-                       wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
-                                               GFP_KERNEL);
-                       wdev->conn->params.ie = wdev->conn->ie;
-                       if (!wdev->conn->ie) {
-                               kfree(wdev->conn);
-                               wdev->conn = NULL;
-                               return -ENOMEM;
-                       }
-               }
-
-               if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
-                       wdev->conn->auto_auth = true;
-                       /* start with open system ... should mostly work */
-                       wdev->conn->params.auth_type =
-                               NL80211_AUTHTYPE_OPEN_SYSTEM;
-               } else {
-                       wdev->conn->auto_auth = false;
-               }
-
-               memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-               wdev->ssid_len = connect->ssid_len;
-               wdev->conn->params.ssid = wdev->ssid;
-               wdev->conn->params.ssid_len = connect->ssid_len;
-
-               /* see if we have the bss already */
-               bss = cfg80211_get_conn_bss(wdev);
-
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-               wdev->connect_keys = connkeys;
-
-               if (prev_bssid) {
-                       memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
-                       wdev->conn->prev_bssid_valid = true;
-               }
-
-               /* we're good if we have a matching bss struct */
-               if (bss) {
-                       wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
-                       err = cfg80211_conn_do_work(wdev);
-                       cfg80211_put_bss(wdev->wiphy, bss);
-               } else {
-                       /* otherwise we'll need to scan for the AP first */
-                       err = cfg80211_conn_scan(wdev);
-                       /*
-                        * If we can't scan right now, then we need to scan again
-                        * after the current scan finished, since the parameters
-                        * changed (unless we find a good AP anyway).
-                        */
-                       if (err == -EBUSY) {
-                               err = 0;
-                               wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
-                       }
-               }
-               if (err) {
-                       kfree(wdev->conn->ie);
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       wdev->connect_keys = NULL;
-                       wdev->ssid_len = 0;
-               }
-
-               return err;
-       } else {
-               wdev->sme_state = CFG80211_SME_CONNECTING;
-               wdev->connect_keys = connkeys;
+       if (!rdev->ops->connect)
+               err = cfg80211_sme_connect(wdev, connect, prev_bssid);
+       else
                err = rdev_connect(rdev, dev, connect);
-               if (err) {
-                       wdev->connect_keys = NULL;
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       return err;
-               }
 
-               memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-               wdev->ssid_len = connect->ssid_len;
-
-               return 0;
+       if (err) {
+               wdev->connect_keys = NULL;
+               wdev->ssid_len = 0;
+               return err;
        }
+
+       return 0;
 }
 
 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
@@ -919,78 +946,17 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
 
        ASSERT_WDEV_LOCK(wdev);
 
-       if (wdev->sme_state == CFG80211_SME_IDLE)
-               return -EINVAL;
-
        kfree(wdev->connect_keys);
        wdev->connect_keys = NULL;
 
-       if (!rdev->ops->disconnect) {
-               if (!rdev->ops->deauth)
-                       return -EOPNOTSUPP;
-
-               /* was it connected by userspace SME? */
-               if (!wdev->conn) {
-                       cfg80211_mlme_down(rdev, dev);
-                       goto disconnect;
-               }
-
-               if (wdev->sme_state == CFG80211_SME_CONNECTING &&
-                   (wdev->conn->state == CFG80211_CONN_SCANNING ||
-                    wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
-                       wdev->sme_state = CFG80211_SME_IDLE;
-                       kfree(wdev->conn->ie);
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       wdev->ssid_len = 0;
-                       return 0;
-               }
-
-               /* wdev->conn->params.bssid must be set if > SCANNING */
-               err = cfg80211_mlme_deauth(rdev, dev,
-                                            wdev->conn->params.bssid,
-                                            NULL, 0, reason, false);
-               if (err)
-                       return err;
+       if (wdev->conn) {
+               err = cfg80211_sme_disconnect(wdev, reason);
+       } else if (!rdev->ops->disconnect) {
+               cfg80211_mlme_down(rdev, dev);
+               err = 0;
        } else {
                err = rdev_disconnect(rdev, dev, reason);
-               if (err)
-                       return err;
        }
 
- disconnect:
-       if (wdev->sme_state == CFG80211_SME_CONNECTED)
-               __cfg80211_disconnected(dev, NULL, 0, 0, false);
-       else if (wdev->sme_state == CFG80211_SME_CONNECTING)
-               __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
-                                         WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                         wextev, NULL);
-
-       return 0;
-}
-
-void cfg80211_sme_disassoc(struct net_device *dev,
-                          struct cfg80211_internal_bss *bss)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
-       u8 bssid[ETH_ALEN];
-
-       ASSERT_WDEV_LOCK(wdev);
-
-       if (!wdev->conn)
-               return;
-
-       if (wdev->conn->state == CFG80211_CONN_IDLE)
-               return;
-
-       /*
-        * Ok, so the association was made by this SME -- we don't
-        * want it any more so deauthenticate too.
-        */
-
-       memcpy(bssid, bss->pub.bssid, ETH_ALEN);
-
-       cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
-                            WLAN_REASON_DEAUTH_LEAVING, false);
+       return err;
 }
index 23fafea..e1534ba 100644 (file)
@@ -1911,24 +1911,46 @@ TRACE_EVENT(cfg80211_send_rx_assoc,
                  NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_deauth,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DECLARE_EVENT_CLASS(netdev_frame_event,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __dynamic_array(u8, frame, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               memcpy(__get_dynamic_array(frame), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+                 NETDEV_PR_ARG,
+                 le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_disassoc,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_unprot_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_deauth,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+DEFINE_EVENT(netdev_frame_event, cfg80211_rx_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len)
 );
 
-DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_disassoc,
-       TP_PROTO(struct net_device *netdev),
-       TP_ARGS(netdev)
+TRACE_EVENT(cfg80211_tx_mlme_mgmt,
+       TP_PROTO(struct net_device *netdev, const u8 *buf, int len),
+       TP_ARGS(netdev, buf, len),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __dynamic_array(u8, frame, len)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               memcpy(__get_dynamic_array(frame), buf, len);
+       ),
+       TP_printk(NETDEV_PR_FMT ", ftype:0x%.2x",
+                 NETDEV_PR_ARG,
+                 le16_to_cpup((__le16 *)__get_dynamic_array(frame)))
 );
 
 DECLARE_EVENT_CLASS(netdev_mac_evt,
index a53f840..14c9a25 100644 (file)
@@ -89,7 +89,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
 
        wdev_lock(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                bool event = true;
 
                if (wdev->wext.connect.channel == chan) {
@@ -188,7 +188,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
 
        err = 0;
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                bool event = true;
 
                if (wdev->wext.connect.ssid && len &&
@@ -277,7 +277,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
 
        wdev_lock(wdev);
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                err = 0;
                /* both automatic */
                if (!bssid && !wdev->wext.connect.bssid)
@@ -364,7 +364,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
        wdev->wext.ie = ie;
        wdev->wext.ie_len = ie_len;
 
-       if (wdev->sme_state != CFG80211_SME_IDLE) {
+       if (wdev->conn) {
                err = cfg80211_disconnect(rdev, dev,
                                          WLAN_REASON_DEAUTH_LEAVING, false);
                if (err)
index ab2bb42..8884399 100644 (file)
@@ -163,6 +163,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
                skb->sp->xvec[skb->sp->len++] = x;
 
                spin_lock(&x->lock);
+               if (unlikely(x->km.state == XFRM_STATE_ACQ)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
+                       goto drop_unlock;
+               }
+
                if (unlikely(x->km.state != XFRM_STATE_VALID)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID);
                        goto drop_unlock;
index c721b0d..80cd1e5 100644 (file)
@@ -44,6 +44,7 @@ static const struct snmp_mib xfrm_mib_list[] = {
        SNMP_MIB_ITEM("XfrmOutPolError", LINUX_MIB_XFRMOUTPOLERROR),
        SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR),
        SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID),
+       SNMP_MIB_ITEM("XfrmAcquireError", LINUX_MIB_XFRMACQUIREERROR),
        SNMP_MIB_SENTINEL
 };